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