; 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}