; SPDX-FileCopyrightText: 2023 Jummit
;
; SPDX-License-Identifier: GPL-3.0-or-later

(local {: get-possible-actions} (require :match.actions))
(local {: invert-state} (require :match.state))
(local {: view} (require :fennel))
(local {: get-at} (require :get_at))

; Observations

(fn pos->id [x y]
  (+ x (* y 10)))

(fn set-link-chance [mind x y d]
  "Change how likely the card at the given coordinates is a link by
the given factor."
  (let [id (pos->id x y)]
    (tset mind id d)))

(fn change-link-chance [mind x y d]
  "Change how likely the card at the given coordinates is a link by
the given factor."
  (let [id (pos->id x y)]
    (tset mind id (+ (or (. mind id) 0) d))))

(fn link-chance [mind x y]
  (or (. mind (pos->id x y)) 0))

(fn small-steps [action state mind]
  "Thinks line-boosted cards which don't go within capture range are links.")

(fn protected [action state mind]
  "Thinks cards which are protected with a fire wall are links.")

(fn taunt [action state mind]
  "Thinks cards which go within capture range are viruses."
  (case action
    {:kind :move : to-x : to-y}
    (each [_ my-action (ipairs (get-possible-actions state))]
      (match my-action {:kind :move : to-x : to-y} (change-link-chance mind to-x to-y -1)))))

(fn guess [action state mind]
  "Randomly guesses links and virus cards."
  1)

(fn deduce [action state mind]
  "Deduces which cards are links by looking at the captured cards."
  1)

; Strategies

(fn random [action state mind]
  "Pick random moves."
  (math.random))

(fn chase [action state mind]
  "Chase cards which are thought to be links."
  1)

(fn side-rush [action state mind]
  "Capture the cards on the sides with a virus card."
  1)

(fn attack [action state mind]
  "Capture cards which are thought to be links."
  (case action
    {:kind :move : to-x : to-y}
    (when (get-at state.online to-x to-y)
      (link-chance mind to-x to-y))))

(fn block [action state mind]
  "Protects links which are threatened by viruses with a fire wall."
  1)

(fn forward [action state mind]
  "Going forward is good."
  1)

(fn block-core [action state mind]
  "Keep cards in front of the core."
  1)

(fn infiltrate [action state mind]
  "Thinks infiltrating is good."
  (when (= action.kind :infiltrate) 1))

(fn fire-wall [action state mind]
  "Play fire walls."
  (when (= action.kind :fire-wall) 1))

(fn stepping-stone [action state mind]
  "Place fire wall near exit"
  1)

(fn enemy-can-capture [state x y]
  (var can-capture false)
  (each [_ action (ipairs (get-possible-actions (invert-state state)))]
    (match action {:kind :move :to-x x :to-y y} (set can-capture true)))
  can-capture)

(fn evade [action state mind]
  "Move link cards away when opponent can capture them."
  (case action
    {:kind :move : from-x : from-y : to-x : to-y}
    (when (and (= (. (get-at state.online from-x from-y) :kind) :link)
               (enemy-can-capture state from-x from-y)
               (not (enemy-can-capture state to-x to-y)))
      1)))

(fn avoid-danger [action state mind]
  "don't move near enemy"
  1)

(fn weigh [strategy weight]
  "Scales the influence of the strategy by the given factor."
  (fn [action state mind]
    (* (or (strategy action state mind) 0) weight)))

(fn doubt [strategy weight]
  "Sometimes ignores a strategy.
Weight 0 never doubts, 1 always doubts."
  (fn [action state mind]
    (when (< weight (math.random))
      (strategy action state mind))))

; Setups

(fn random-setup [_x _y] (math.random))

(fn setup-viruses-near-center [x _y]
  (math.abs (- x 4)))

; Assumptions

(fn center-viruses [state mind]
  "Assumes that the player places more viruses in the center."
  (each [_ {: mine : x : y} (ipairs state.online)]
    (when (not mine)
      (change-link-chance mind x y (- (math.abs (- x 4)) 2)))))

; AIs

(fn ai [setup
        assumptions
        observations
        strategies
        greeting
        commentate
        reply
        won-message
        lost-message]
  "Returns a constructor for an AI with the given strategy.
The AI has the following methods:
(fn setup [x y]
  \"Returns a higher number when a link should be placed on the cell.\")
(fn turn [state opponent-action]
  \"Returns the action the AI takes in this turn.\")
(fn commentate [action state]
  \"Returns the message the opponent says about their own action.\")
(fn reply [action state]
  \"Returns the message the opponent says about the opponents action.\")"
  (fn []
    (local mind {})
    (var first true)

    (fn turn [state opponent-action]
      (when first
        (each [_ assumption (ipairs assumptions)]
          (assumption state mind))
        (set first false))
      (case opponent-action
        {:kind :move : to-x : to-y : from-x : from-y}
        (let [chance (link-chance mind from-x from-y)]
          (set-link-chance mind from-x from-y 0)
          (set-link-chance mind to-x to-y chance)))
      (each [_ observe (ipairs observations)]
        (observe opponent-action state state mind))
      (let [ratings (icollect [_ action (ipairs (get-possible-actions state))]
                      {: action
                       :rating (accumulate [score 0 _ strategy (ipairs strategies)]
                                 (+ score (or (strategy action state mind) 0)))})]
        (table.sort ratings (fn [a b] (< b.rating a.rating)))
        (. ratings 1 :action)))

    {: setup
     : turn
     : commentate
     : reply
     : greeting
     : won-message
     : lost-message}))

(local mayuri (ai random-setup [center-viruses] [taunt]
                  [(weigh random 0.1) (weigh fire-wall -1) attack evade] "Uhm... Mayuri doesn't
know how to play this
game!"
                  (fn [action state]) (fn [action state])
                  "Mayuri... Won? Horray!" "Is it over?"))

(local okabe (ai random-setup [center-viruses] [taunt]
                 [avoid-danger infiltrate side-rush] "Operation
JAM go!" (fn [action state]) (fn [action state]) "I win!" "Nooo!"))

(local makise (ai random-setup [center-viruses] [protected taunt] 
                  [evade avoid-danger infiltrate side-rush] "Mmmh...
how should I
do this..." (fn [action state]) (fn [action state]) "I win!" "Well played."))

(local feyris (ai random-setup [center-viruses] [small-steps protected taunt] 
                  [evade
                   avoid-danger
                   infiltrate
                   stepping-stone
                   side-rush
                   block] "Prepare to
loose! Nya!" (fn [action state]) (fn [action state]) "I win!" "Impossible!"))

{: mayuri : okabe : makise : feyris}