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

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.

Step by Step Solution

3.36 Rating (146 Votes )

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock

The issue seems to be that the direction of the snakes head isnt being properly saved and restored from the game state when reversing You already have a GameState case class that includes the directio... View full answer

blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Programming Questions!