35 lines
1.3 KiB
Scala
35 lines
1.3 KiB
Scala
package snake
|
|
|
|
/** m03 rules: the pure game rules. Each function takes a state and returns a new one. */
|
|
object Rules:
|
|
|
|
def move(p: Point, dir: Direction): Point =
|
|
val d = Model.delta(dir)
|
|
Point(p.x + d.x, p.y + d.y)
|
|
|
|
def inBounds(state: GameState, p: Point): Boolean =
|
|
p.x >= 0 && p.x < state.width && p.y >= 0 && p.y < state.height
|
|
|
|
// Validate against `dir` (last moved), not `nextDir`: Up then Left within one tick must not fold the snake back on itself.
|
|
def turn(state: GameState, dir: Direction): GameState =
|
|
if state.status == Status.Playing && dir != Model.opposite(state.dir) then state.copy(nextDir = dir)
|
|
else state
|
|
|
|
def advance(state: GameState): GameState =
|
|
if state.status != Status.Playing then state
|
|
else
|
|
val d = state.nextDir
|
|
val head = move(state.snake.head, d)
|
|
val eating = state.apple.contains(head)
|
|
// When not eating, the tail leaves its cell this step, so the head may move into it.
|
|
val body = if eating then state.snake else Util.dropLast(state.snake)
|
|
if !inBounds(state, head) || Util.contains(body, head, _ == _) then
|
|
state.copy(dir = d, status = Status.GameOver)
|
|
else
|
|
state.copy(
|
|
snake = head :: body,
|
|
dir = d,
|
|
apple = if eating then None else state.apple,
|
|
score = state.score + (if eating then 1 else 0)
|
|
)
|