Hi, I am working on a Scala assignment on a snake game with a reverse mode. While
Question:
Hi, I am working on a Scala assignment on a snake game with a reverse mode. While playing the game it saves the state (positions of everything) in a list so that once 'r' is pressed it will reverse to exactly that state. However, I can't get the direction the head is pointing to change immediately back to the state that should be in that list. Say if during gameplay it was heading east but then the next move the snake is going south, the reverse function has the head pointing south when it reaches the cell it should already be pointing east at. I wanted to ask if someone could modify my code so that the game state will correctly save the direction the head is pointing in for that exact state so that it reverses this accordingly when pressing r.
This is my code:
package snake.logic
import engine.random.{RandomGenerator, ScalaRandomGen}
import scala.collection.immutable.List
class GameLogic(val random: RandomGenerator, val gridDims: Dimensions) {
private var gameStates: List[GameState] = List()
private var reversing: Boolean = false
// Initialize the snake, apple, and other variables
var snake: List[Point] = List(Point(2, 0), Point(1, 0), Point(0, 0))
var apple: Point = generateAppleSpot()
var direction: Direction = East()
var directionChanged: Boolean = false
def gameOver: Boolean = snake.tail.contains(snake.head)
def step(): Unit = {
val growthPerApple = 3
if (reversing) {
// Reverse steps one by one
if (gameStates.nonEmpty) {
val gameState = gameStates.head
snake = gameState.snake
apple = gameState.apple
direction = gameState.direction
gameStates = gameStates.tail
directionChanged = true
} else {
reversing = false
}
} else if (!gameOver) { // Check for !gameOver here to allow reversal even when gameOver is reached
// Add the current state to the list of game states only when not reversing
if (!reversing) {
gameStates = GameState(snake, apple, direction) :: gameStates
}
val forwardMove = getNextSpot()
snake = wrapAround(forwardMove) :: snake.init
if (snake.head == apple) {
snake = snake ++ List.fill(growthPerApple)(snake.last)
apple = generateAppleSpot()
}
}
directionChanged = false
}
def changeDir(d: Direction): Unit = {
if (direction != d.opposite && !directionChanged) {
direction = d
directionChanged = true
}
}
def getCellType(p: Point): CellType = {
if (snake.head == p)
SnakeHead(direction)
else if (snake.contains(p))
SnakeBody(snake.indexOf(p).toFloat / snake.length.toFloat)
else if (apple == p)
Apple()
else
Empty()
}
def setReverse(r: Boolean): Unit = {
if (r && !reversing) {
// Start reversing from the current state
reversing = true
directionChanged = true
} else if (!r && reversing) {
// Stop reversing when 'r' is released
reversing = false
// Remove the current state from gameStates if it was recorded while reversing
if (gameStates.nonEmpty && gameStates.head == GameState(snake, apple, direction)) {
gameStates = gameStates.tail
}
}
// When not in reverse mode, record the current state
if (!reversing) {
gameStates = GameState(snake, apple, direction) :: gameStates
}
}
def getNextSpot(): Point = {
direction match {
case East() => Point(snake.head.x + 1, snake.head.y)
case North() => Point(snake.head.x, snake.head.y - 1)
case West() => Point(snake.head.x - 1, snake.head.y)
case South() => Point(snake.head.x, snake.head.y + 1)
}
}
def generateAppleSpot(): Point = {
val availableSpots = (0 until (gridDims.width * gridDims.height))
.map(index => Point(index % gridDims.width, index / gridDims.width))
.filter(cell => !snake.contains(cell))
if (availableSpots.isEmpty) Point(-1, -1) else {
val randomIndex = random.randomInt(availableSpots.length)
availableSpots(randomIndex)
}
}
def wrapAround(p: Point): Point = {
val wrappedX = (p.x + gridDims.width) % gridDims.width
val wrappedY = (p.y + gridDims.height) % gridDims.height
Point(wrappedX, wrappedY)
}
}
// New class to represent the game state
case class GameState(snake: List[Point], apple: Point, direction: Direction)
/** GameLogic companion object */
object GameLogic {
val FramesPerSecond: Int = 5
val DrawSizeFactor = 1.0
val DefaultGridDims: Dimensions = Dimensions(width = 25, height = 25)
}
And this is one of the diagnostics that shows the head to be pointing the wrong direction (step 8, specifically):
step=0, rand=2, actions=<>
Want | Got ✓
-------------
OO>.. | OO>..
A.... | A....
step=1, rand=2, actions=
Want | Got ✓
-------------
.OO>. | .OO>.
A.... | A....
step=2, rand=2, actions=
Want | Got ✓
-------------
..OO> | ..OO>
A.... | A....
step=3, rand=2, actions=
Want | Got ✓
-------------
...OO | ...OO
A...^ | A...^
step=4, rand=2, actions=
Want | Got ✓
-------------------------------------
GameOverDisplay() | GameOverDisplay()
step=5, rand=2, actions=
Want | Got ✓
-------------------------------------
GameOverDisplay() | GameOverDisplay()
step=6, rand=2, actions=
Want | Got ✓
-------------------------------------
GameOverDisplay() | GameOverDisplay()
step=7, rand=2, actions=
Want | Got ✓
-------------
...OO | ...OO
A...^ | A...^
step=8, rand=2, actions=
Want | Got ✗
-------------
..OO> | ..OO^
A.... | A....
step=9, rand=2, actions=
Want | Got ✓
-------------
.OO>. | .OO>.
A.... | A....
step=10, rand=2, actions=
Want | Got ✓
-------------
OO>.. | OO>..
A.... | A....
So yes, can someone please help me fix this? Don't bother using chatGPT, if that would have helped I wouldn't be here. The code for graphics and stuff like that is quite long so I didn't provide that as I don't think it matters, but I will happily provide more context if needed.