From 900717178796e4c6fbde370c498915639d00f384 Mon Sep 17 00:00:00 2001 From: m Date: Thu, 6 Nov 2025 16:06:37 -0500 Subject: [PATCH 1/2] speed control --- internal/game/GameManager.go | 10 ++++++++++ internal/game/Player.go | 38 ++++++++++++++++++++---------------- internal/ui/GameView.go | 12 +++++++++--- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/internal/game/GameManager.go b/internal/game/GameManager.go index 40c04b8..d2b8a29 100644 --- a/internal/game/GameManager.go +++ b/internal/game/GameManager.go @@ -135,6 +135,16 @@ func (gm *GameManager) processGameTick() { return true } + if player.Speed < 0 { + if player.ticksSkippedCount < player.Speed*-1 { + // we are skipping this tick because we are slow + player.ticksSkippedCount += 1 + return true + } + + player.ticksSkippedCount = 0 + } + nextTile := player.GetNextTile() if IsWall(nextTile.Y, nextTile.X) { player.isDead = true diff --git a/internal/game/Player.go b/internal/game/Player.go index ba82a3f..872b878 100644 --- a/internal/game/Player.go +++ b/internal/game/Player.go @@ -19,19 +19,21 @@ type AllTiles struct { } type Player struct { - Name string - SshSession ssh.Session - Color *int - ClaimedEstate int - Location *Tile - CurrentDirection Direction - UpdateChannel chan tea.Msg - BotStrategy Strategy - Kills int - isDead bool - isSafe bool - Tail Tail - AllTiles AllTiles + Name string + SshSession ssh.Session + Color *int + ClaimedEstate int + Location *Tile + CurrentDirection Direction + UpdateChannel chan tea.Msg + BotStrategy Strategy + Kills int + isDead bool + isSafe bool + Speed int + ticksSkippedCount int //this is used if speed is below 0 + Tail Tail + AllTiles AllTiles } func CreateNewPlayer(sshSession ssh.Session, name string, color int, spawnPoint *Tile) *Player { @@ -54,10 +56,12 @@ func CreateNewPlayer(sshSession ssh.Session, name string, color int, spawnPoint tailTiles: []*Tile{ spawnPoint, }}, - UpdateChannel: make(chan tea.Msg, 256), - Kills: 0, - isDead: false, - isSafe: false, + UpdateChannel: make(chan tea.Msg, 256), + Kills: 0, + isDead: false, + isSafe: false, + Speed: 0, + ticksSkippedCount: 0, } } diff --git a/internal/ui/GameView.go b/internal/ui/GameView.go index 7f9533f..2dcd79f 100644 --- a/internal/ui/GameView.go +++ b/internal/ui/GameView.go @@ -117,11 +117,17 @@ func (m GameViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } - if (engineCommand.Dx == -currentPlayer.CurrentDirection.Dx && engineCommand.Dy == -currentPlayer.CurrentDirection.Dy) || (engineCommand == currentPlayer.CurrentDirection) { - return m, nil + oldSpeed := currentPlayer.Speed + if engineCommand.Dx == -currentPlayer.CurrentDirection.Dx && engineCommand.Dy == -currentPlayer.CurrentDirection.Dy { + currentPlayer.Speed -= 1 + } + + if engineCommand == currentPlayer.CurrentDirection { + currentPlayer.Speed += 1 } - if (engineCommand == game.Direction{}) { + //speed was altered no need to bather with direction switch + if oldSpeed != currentPlayer.Speed { return m, nil } From eccca4a2fab7def074641fff3716f8f0ec7fd9b6 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 7 Nov 2025 11:05:32 -0500 Subject: [PATCH 2/2] speed control --- Readme.md | 7 +++ internal/game/GameManager.go | 112 ++++++++++++++++++----------------- internal/game/Player.go | 47 +++++++++------ internal/ui/GameView.go | 6 +- 4 files changed, 99 insertions(+), 73 deletions(-) diff --git a/Readme.md b/Readme.md index c29c77c..3ed7c98 100644 --- a/Readme.md +++ b/Readme.md @@ -7,13 +7,20 @@ sshOurboros -- multiplayer game where snake eats itself and grows *To play go here `ssh web2u.org -p6996`* *The Rules:* + 1. The goal is to claim the most space 2. Secondary goal is to kill as many other snakes as you can + *The how:* + 1. To claim space you need to either eat your own tail or reach tiles you've already claimed, tiles that are enclosed when you do so become yours! 2. To kill other snakes you hit their tails +3. To speed up press arrow of the same direction you going toward mulitple times +4. To slow down either press "space" or the opposite direction to gradually slow down + *To watchout:* + 1. Other players can kill you 2. Other players can take your tiles. diff --git a/internal/game/GameManager.go b/internal/game/GameManager.go index d2b8a29..825204c 100644 --- a/internal/game/GameManager.go +++ b/internal/game/GameManager.go @@ -145,80 +145,82 @@ func (gm *GameManager) processGameTick() { player.ticksSkippedCount = 0 } - nextTile := player.GetNextTile() - if IsWall(nextTile.Y, nextTile.X) { - player.isDead = true - gm.PlayerManager.SunsetPlayersChannel <- player - return true - } - - player.isSafe = false - - if nextTile.OwnerColor != nil && nextTile.OwnerColor != player.Color { - nextTileOwnerAny, _ := gm.Players.Load(*nextTile.OwnerColor) - if nextTileOwnerAny == nil { + nextTiles := player.GetNextTiles() + for _, nextTile := range nextTiles { + if IsWall(nextTile.Y, nextTile.X) { + player.isDead = true + gm.PlayerManager.SunsetPlayersChannel <- player return true } - nextTileOwner := nextTileOwnerAny.(*Player) - if nextTileOwner.isDead || nextTileOwner.isSafe { - return true + player.isSafe = false + + if nextTile.OwnerColor != nil && nextTile.OwnerColor != player.Color { + nextTileOwnerAny, _ := gm.Players.Load(*nextTile.OwnerColor) + if nextTileOwnerAny == nil { + return true + } + + nextTileOwner := nextTileOwnerAny.(*Player) + if nextTileOwner.isDead || nextTileOwner.isSafe { + return true + } + + if nextTileOwner.Location == nextTile { + nextTileOwner.isDead = true + player.isDead = true + player.Kills += 1 + nextTileOwner.Kills += 1 + + gm.PlayerManager.SunsetPlayersChannel <- nextTileOwner + gm.PlayerManager.SunsetPlayersChannel <- player + return true + } + + if nextTile.IsTail { + nextTileOwner.isDead = true + gm.PlayerManager.SunsetPlayersChannel <- nextTileOwner + + player.Kills += 1 + nextTile.OwnerColor = player.Color + nextTile.IsTail = true + player.Tail.tailLock.Lock() + player.Tail.tailTiles = append(player.Tail.tailTiles, nextTile) + player.Tail.tailLock.Unlock() + + player.Location = nextTile + + return true + } + } - if nextTileOwner.Location == nextTile { - nextTileOwner.isDead = true - player.isDead = true - player.Kills += 1 - nextTileOwner.Kills += 1 + if nextTile.OwnerColor == player.Color && len(player.Tail.tailTiles) > 0 { + select { + case gm.SpaceFillerService.SpaceFillerChan <- player: + // Successfully sent direction + default: + gm.SpaceFillerService = getNewSpaceFiller(gm.GameMap) + log.Printf("space fill channel is full") + } - gm.PlayerManager.SunsetPlayersChannel <- nextTileOwner - gm.PlayerManager.SunsetPlayersChannel <- player + player.Location = nextTile + player.isSafe = true return true } - if nextTile.IsTail { - nextTileOwner.isDead = true - gm.PlayerManager.SunsetPlayersChannel <- nextTileOwner - - player.Kills += 1 + if nextTile.OwnerColor != player.Color { nextTile.OwnerColor = player.Color nextTile.IsTail = true + nextTile.Direction = player.CurrentDirection player.Tail.tailLock.Lock() player.Tail.tailTiles = append(player.Tail.tailTiles, nextTile) player.Tail.tailLock.Unlock() - - player.Location = nextTile - - return true - } - - } - - if nextTile.OwnerColor == player.Color && len(player.Tail.tailTiles) > 0 { - select { - case gm.SpaceFillerService.SpaceFillerChan <- player: - // Successfully sent direction - default: - gm.SpaceFillerService = getNewSpaceFiller(gm.GameMap) - log.Printf("space fill channel is full") } player.Location = nextTile - player.isSafe = true - return true - } - - if nextTile.OwnerColor != player.Color { - nextTile.OwnerColor = player.Color - nextTile.IsTail = true - nextTile.Direction = player.CurrentDirection - player.Tail.tailLock.Lock() - player.Tail.tailTiles = append(player.Tail.tailTiles, nextTile) - player.Tail.tailLock.Unlock() } - player.Location = nextTile - if player.BotStrategy != nil { gm.BotStrategyWg.Add(1) go func() { diff --git a/internal/game/Player.go b/internal/game/Player.go index 872b878..0f1d1d3 100644 --- a/internal/game/Player.go +++ b/internal/game/Player.go @@ -65,26 +65,35 @@ func CreateNewPlayer(sshSession ssh.Session, name string, color int, spawnPoint } } -func (p *Player) GetNextTile() *Tile { - nextX := p.Location.X + p.CurrentDirection.Dx - nextY := p.Location.Y + p.CurrentDirection.Dy - - if nextX < 0 { - nextX = MapColCount - 1 - } else if nextX >= MapColCount { - nextX = 0 - } - if nextY < 0 { - nextY = MapRowCount - 1 - } else if nextY >= MapRowCount { - nextY = 0 - } +func (p *Player) GetNextTiles() []*Tile { + tilesToGet := max(1, p.Speed) + currLocationX := p.Location.X + currLocationY := p.Location.Y + result := []*Tile{} + + for range tilesToGet { + nextX := currLocationX + p.CurrentDirection.Dx + nextY := currLocationY + p.CurrentDirection.Dy + + if nextX < 0 { + nextX = MapColCount - 1 + } else if nextX >= MapColCount { + nextX = 0 + } + if nextY < 0 { + nextY = MapRowCount - 1 + } else if nextY >= MapRowCount { + nextY = 0 + } - if nextY < MapRowCount && nextX < MapColCount { - return getInitGameMap()[nextY][nextX] + if nextY < MapRowCount && nextX < MapColCount { + result = append(result, getInitGameMap()[nextY][nextX]) + currLocationX = nextX + currLocationY = nextY + } } - return nil + return result } func (p *Player) resetTailData() { @@ -116,3 +125,7 @@ func (p *Player) GetConsolidateTiles() float64 { p.AllTiles.AllPlayerTiles = updatedTiles return claimedLand } + +func (p *Player) ResetSpeed() { + p.Speed = 0 +} diff --git a/internal/ui/GameView.go b/internal/ui/GameView.go index 2dcd79f..780b01f 100644 --- a/internal/ui/GameView.go +++ b/internal/ui/GameView.go @@ -113,6 +113,8 @@ func (m GameViewModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { engineCommand = game.Direction{Dx: -1, Dy: 0, PlayerColor: *currentPlayer.Color} case "d", "right": engineCommand = game.Direction{Dx: 1, Dy: 0, PlayerColor: *currentPlayer.Color} + case " ": + currentPlayer.ResetSpeed() default: return m, nil } @@ -385,9 +387,11 @@ func (m GameViewModel) renderStatusPanel(currentPlayer *game.Player, width int, // (This section remains unchanged from your original code) statusContent.WriteString(lipgloss.NewStyle().Bold(true).Render("--- Player Stats ---\n")) claimedLand := currentPlayer.GetConsolidateTiles() + statusContent.WriteString(fmt.Sprintf("Direction: %c\n", headRunes[game.Direction{Dx: currentPlayer.CurrentDirection.Dx, Dy: currentPlayer.CurrentDirection.Dy}])) + statusContent.WriteString(fmt.Sprintf("Speed: %d \n", currentPlayer.Speed)) + statusContent.WriteString(fmt.Sprintf("Kills: %d\n", currentPlayer.Kills)) statusContent.WriteString(fmt.Sprintf("Claimed: %.2f %% of land\n", claimedLand*100/float64(game.MapColCount*game.MapColCount))) - statusContent.WriteString(fmt.Sprintf("Direction: %c\n", headRunes[game.Direction{Dx: currentPlayer.CurrentDirection.Dx, Dy: currentPlayer.CurrentDirection.Dy}])) statusContent.WriteString("\n") botCount := 0