; SPDX-FileCopyrightText: 2023 Jummit
;
; SPDX-License-Identifier: GPL-3.0-or-later
;; Analyse the game state for possible actions. Defines the game rules.
(local {: get-at} (require :get_at))
(fn get-moves [state x y range moves?]
"Returns where a card could move from the given position.
Positions are returned as tables containing two numbers."
(let [moves (or moves? [])]
(each [_ dir (ipairs [[0 -1] [0 1] [1 0] [-1 0]])]
(let [tx (+ x (. dir 1))
ty (+ y (. dir 2))
cell (get-at state.cells tx ty)
online (get-at state.online tx ty)
id (+ tx (* ty 10))]
(when (and cell (or (not cell.fire-wall) (= cell.fire-wall :mine))
(or (not cell.exit) (= cell.exit :mine))
(or (not online) (not online.mine)))
(tset moves id [tx ty])
(when (and (< 1 range) (not online))
(get-moves state tx ty (- range 1) moves)))))
(icollect [_ move (pairs moves)] move)))
(fn get-possible-actions [state]
"Returns a list of actions the player can take from a given state."
(local actions [])
(each [_ {: x : y : mine : boosted} (ipairs state.online)]
(when mine
(when boosted
(table.insert actions {:kind :remove-line-boost : x : y}))
(each [_ [tx ty] (ipairs (get-moves state x y (if boosted 2 1)))]
(when (or (not= x tx) (not= y ty))
(table.insert actions (if (. (get-at state.cells tx ty) :core)
{:kind :infiltrate : x : y}
{:kind :move
:from-x x
:from-y y
:to-x tx
:to-y ty}))))))
(each [_ {: x : y : fire-wall} (ipairs state.cells)]
(when (= fire-wall :mine)
(table.insert actions {:kind :remove-fire-wall : x : y})))
(let [{: fire-wall :404 not-found : line-boost : virus-check} state.terminals]
(when fire-wall
(each [_ {: x : y &as cell} (ipairs state.cells)]
(let [online (get-at state.online x y)]
(when (and (not cell.fire-wall) (or (not online) online.mine))
(table.insert actions {:kind :fire-wall : x : y})))))
(when virus-check
(each [_ {: x : y : mine : kind} (ipairs state.online)]
(when (and (not mine) (not kind))
(table.insert actions {:kind :virus-check : x : y}))))
(when line-boost
(each [_ {: x : y : mine : boosted} (ipairs state.online)]
(when (and mine (not boosted))
(table.insert actions {:kind :line-boost : x : y}))))
(when not-found
(each [_ {:x xa :y ya : mine-a} (ipairs state.online)]
(when mine-a
(each [_ {: xb : yb : mine-b} (ipairs state.online)]
(when (and mine-b (not (and (= xa xb) (= ya yb))))
(table.insert actions {:kind :404 : xa : ya : xb : yb})))))))
actions)
{: get-possible-actions}