feat: add moving phase
also moves `moves` into the game object and moves updating `moves` to game:updatemain
parent
7776b2011a
commit
f985dc4e5c
|
@ -1,19 +1,21 @@
|
|||
(local {: contains} (require :lib.contains))
|
||||
(local {: flip} (require :lib.flip))
|
||||
(local {: head} (require :lib.head))
|
||||
(local {: keys} (require :lib.keys))
|
||||
(local {: kvflip} (require :lib.kvflip))
|
||||
(local {: mill-at?} (require :lib.mill))
|
||||
(local {: pprint} (require :lib.tableprint))
|
||||
(local {: slice} (require :lib.slice))
|
||||
(local {: space-is-neighbor?} (require :lib.space-is-neighbor))
|
||||
(local {: tail} (require :lib.tail))
|
||||
|
||||
{
|
||||
: contains
|
||||
: flip
|
||||
: head
|
||||
: keys
|
||||
: kvflip
|
||||
: mill-at?
|
||||
: pprint
|
||||
: slice
|
||||
: space-is-neighbor?
|
||||
: tail
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(fn flip [t]
|
||||
(fn kvflip [t]
|
||||
"takes a table of {key value} and returns a table of {value key}"
|
||||
(collect [k v (pairs t)] (values v k)))
|
||||
|
||||
{: flip}
|
||||
{: kvflip}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
(let [{: flip} (require :lib.flip)
|
||||
(let [{: kvflip} (require :lib.kvflip)
|
||||
{: describe :end test-end} (require :lib.test)]
|
||||
(describe "flip()" (fn [t]
|
||||
(describe "kvflip()" (fn [t]
|
||||
(let [input {:apple "red" :banana "yellow"}
|
||||
expected {:red "apple" :yellow "banana"}
|
||||
]
|
||||
(t {:given "a table"
|
||||
:should "flip that table!"
|
||||
:should "kvflip that table!"
|
||||
: expected
|
||||
:actual (flip input)})
|
||||
:actual (kvflip input)})
|
||||
(test-end)))))
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
(local {: contains} (require :lib.contains))
|
||||
(local {: head} (require :lib.head))
|
||||
(local {: tail} (require :lib.tail))
|
||||
|
||||
(lambda space-is-neighbor? [all-neighbors from to]
|
||||
;; i have learned to check that i'm passing the correct type of move
|
||||
;; i.e. a number and not a string
|
||||
(assert (= "number" (type from)) "from must be a number")
|
||||
(assert (= "number" (type to)) "to must be a number")
|
||||
(assert (= "table" (type all-neighbors)) "all-neighbors must be a table")
|
||||
|
||||
(let [neighborhood-list (icollect [_ n (ipairs all-neighbors)] (if (= from (head n)) n))
|
||||
neighborhood (head neighborhood-list)
|
||||
neighbors (tail neighborhood)
|
||||
is-neighbor (contains neighbors to)]
|
||||
is-neighbor))
|
||||
|
||||
{: space-is-neighbor?}
|
|
@ -0,0 +1,22 @@
|
|||
(let [{: space-is-neighbor?} (require :lib.space-is-neighbor)
|
||||
{: neighbors} (require :lib.constants)
|
||||
{: describe :end test-end} (require :lib.test)
|
||||
with-neighbors (partial space-is-neighbor? neighbors)
|
||||
]
|
||||
|
||||
(describe "space-is-neighbor()" (fn [t]
|
||||
(t {:given "space of 3"
|
||||
:should "know 2 is a neighbor"
|
||||
:expected true
|
||||
:actual (with-neighbors 3 2)})
|
||||
(t {:given "space of 3"
|
||||
:should "know 15 is a neighbor"
|
||||
:expected true
|
||||
:actual (with-neighbors 3 15)})
|
||||
(t {:given "space of 3"
|
||||
:should "know 1 is not a neighbor"
|
||||
:expected false
|
||||
:actual (with-neighbors 3 1)})
|
||||
|
||||
(test-end))))
|
||||
|
115
main.fnl
115
main.fnl
|
@ -2,49 +2,50 @@
|
|||
(local {
|
||||
: contains
|
||||
: head
|
||||
: flip
|
||||
: kvflip
|
||||
: pprint
|
||||
: slice
|
||||
:mill-at? mill-at-maker
|
||||
:space-is-neighbor? space-is-neighbor-maker
|
||||
} (require :lib.index))
|
||||
;; constants...more like just strings
|
||||
(local const (require :lib.constants))
|
||||
;; front-loading mill with a partial
|
||||
;; front-loading with some partials
|
||||
(local mill-at? (partial mill-at-maker const.mills))
|
||||
(local space-is-neighbor? (partial space-is-neighbor-maker const.neighbors))
|
||||
|
||||
|
||||
; there are three phases of play:
|
||||
; placing, moving, and flying.
|
||||
; (plus one for capturing)
|
||||
; (plus one for complete)
|
||||
;; there are three phases of play:
|
||||
;; placing, moving, and flying.
|
||||
;; (plus one for capturing)
|
||||
;; (plus one for game-over)
|
||||
(local stages {
|
||||
:placing 1 ;; placing the cows
|
||||
:moving 2 ;; moving the cows
|
||||
:flying 3 ;; flying the cows
|
||||
:capture 4 ;; capture a cow (we do not shoot cows)
|
||||
:complete 5 ;; no more cows!
|
||||
:complete 5 ;; no more cows! jk the cows are fine. the game's just over okay
|
||||
})
|
||||
|
||||
|
||||
; there are two players
|
||||
; their names are LUIGI and MARIO
|
||||
;; there are two players
|
||||
;; their names are LUIGI and MARIO
|
||||
(local player {
|
||||
:one 1 ;; luigi has light cows
|
||||
:two 2 ;; mario has DARK cows >:)
|
||||
})
|
||||
|
||||
|
||||
; initialize moves[] to 0.
|
||||
; this is the game state.
|
||||
; shows which spaces are occupied by which players.
|
||||
; 0 = unoccupied
|
||||
; 1 = Player 1
|
||||
; 2 = Player 2
|
||||
;; TODO: move this to game.moves?
|
||||
(local moves (fcollect [i 1 24] 0))
|
||||
; return the numerical index (1-24) of a [A-Za-z0-9] formatted move
|
||||
(fn index-of-move [m]
|
||||
(let [upper (string.upper m)
|
||||
rev (string.reverse upper)
|
||||
idx (head (icollect [i v (ipairs const.spaces)]
|
||||
(if (or (= v upper) (= v rev)) i)))]
|
||||
idx))
|
||||
|
||||
|
||||
; game state object
|
||||
;; game state object
|
||||
(local game {
|
||||
:player player.one
|
||||
:stage stages.placing
|
||||
|
@ -52,19 +53,51 @@
|
|||
(case self.stage
|
||||
4 ;; capture
|
||||
(do
|
||||
(tset moves move 0)
|
||||
;; TODO: capturing during moving is not working?
|
||||
(tset self.moves (index-of-move move) 0)
|
||||
(tset self :player (self:next-player))
|
||||
(tset self :stage stages.placing)
|
||||
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
||||
(tset self.moves (index-of-move move) self.player)
|
||||
)
|
||||
1 ;; placing
|
||||
(if (mill-at? moves move)
|
||||
(do
|
||||
(set self.pieces-placed (+ 1 self.pieces-placed))
|
||||
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
||||
(tset self.moves (index-of-move move) self.player)
|
||||
(if (mill-at? self.moves (index-of-move move))
|
||||
(tset self :stage stages.capture)
|
||||
(tset self :player (self:next-player))
|
||||
)
|
||||
)
|
||||
2 ;; moving
|
||||
(let [from (index-of-move (string.sub move 1 2))
|
||||
to (index-of-move (string.sub move -2 -1))]
|
||||
(print "From" from)
|
||||
(print "To" to)
|
||||
(tset self.moves from 0)
|
||||
(tset self.moves to self.player)
|
||||
(if (mill-at? self.moves to)
|
||||
(tset self :stage stages.capture)
|
||||
(tset self :player (self:next-player))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
:next-player (fn [self] (if (= player.one self.player) player.two player.one))
|
||||
:pieces-placed 0
|
||||
:init (fn [self]
|
||||
; initialize moves[] to 0.
|
||||
; this is the game state.
|
||||
; shows which spaces are occupied by which players.
|
||||
; 0 = unoccupied
|
||||
; 1 = Player 1
|
||||
; 2 = Player 2
|
||||
; NOTE: I think it might be a good idea to make moves
|
||||
; a list of moves. so that there can be undo and history
|
||||
(set self.moves (fcollect [i 1 24] 0))
|
||||
)
|
||||
})
|
||||
(game:init)
|
||||
|
||||
|
||||
(fn string-upper [s]
|
||||
|
@ -83,7 +116,7 @@
|
|||
(print (string.format row-template (table.unpack myslice)))
|
||||
(set index offset)))
|
||||
(print row))))
|
||||
(print (.. "Stage: " (string-upper (. (flip stages) game.stage))))
|
||||
(print (.. "Stage: " (string-upper (. (kvflip stages) game.stage))))
|
||||
(print (.. "Player " game.player "'s turn:")))
|
||||
|
||||
|
||||
|
@ -100,29 +133,25 @@
|
|||
(fn space-exists? [m] (contains const.spaces (string.upper m)))
|
||||
|
||||
|
||||
; return the numerical index (1-24) of a [A-Za-z0-9] formatted move
|
||||
(fn index-of-move [m]
|
||||
(let [upper (string.upper m)
|
||||
rev (string.reverse upper)
|
||||
idx (head (icollect [i v (ipairs const.spaces)]
|
||||
(if (or (= v upper) (= v rev)) i)))]
|
||||
idx))
|
||||
|
||||
; is the space represented by a [A-Za-z0-9] move unoccupied?
|
||||
(fn space-is-unoccupied? [m]
|
||||
(let [unoccupied? 0] ; i.e. is move equal to 0
|
||||
(= unoccupied? (. moves (index-of-move m)))))
|
||||
(= unoccupied? (. game.moves (index-of-move m)))))
|
||||
|
||||
|
||||
(fn space-is-occupied-by-opponent? [m]
|
||||
(let [opponent (if (= game.player 1) 2 1)
|
||||
result (= opponent (. moves (index-of-move m))) ]
|
||||
result (= opponent (. game.moves (index-of-move m))) ]
|
||||
result))
|
||||
|
||||
|
||||
(fn moving-format? [m]
|
||||
(let [from (string.sub m 1 2)
|
||||
to (string.sub m -2 -1)]
|
||||
(and (space-exists? from) (space-exists? to))))
|
||||
|
||||
; is this a legal move?
|
||||
; TODO: maybe some functional error handling here?
|
||||
; maybe some functional error handling here?
|
||||
; https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch08#pure-error-handling
|
||||
; https://mostly-adequate.gitbook.io/mostly-adequate-guide/appendix_b#either
|
||||
; or maybe all i need is a case-try statement..
|
||||
|
@ -142,9 +171,20 @@
|
|||
(= stages.capture game.stage)
|
||||
(or (space-is-occupied-by-opponent? move)
|
||||
(print "Choose an opponent's piece to remove."))
|
||||
(or (not (mill-at? moves (index-of-move move)))
|
||||
(or (not (mill-at? game.moves (index-of-move move)))
|
||||
(print "Ma'am, it is ILLEGAL to break up a mill."))
|
||||
)
|
||||
(and
|
||||
(= stages.moving game.stage)
|
||||
(or (moving-format? move)
|
||||
(print "Try a move like A1A2 or A7 D7"))
|
||||
(or (not (space-is-occupied-by-opponent? (string.sub move 1 2)))
|
||||
(print "That's not yours, don't touch it."))
|
||||
(or (space-is-unoccupied? (string.sub move -2 -1))
|
||||
(print "That space is occupied!"))
|
||||
(or (space-is-neighbor? (index-of-move (string.sub move 1 2)) (index-of-move (string.sub move -2 -1)))
|
||||
(print "That ain't your neighbor, Johnny"))
|
||||
)
|
||||
(and
|
||||
;; TODO: add flying phase
|
||||
(= stages.flying game.stage)
|
||||
|
@ -161,7 +201,7 @@
|
|||
(fn main []
|
||||
;; game loop
|
||||
(while (not (= game.stage stages.complete))
|
||||
(print-board const.board moves)
|
||||
(print-board const.board game.moves)
|
||||
|
||||
;; validation loop
|
||||
(var is-valid false)
|
||||
|
@ -169,14 +209,11 @@
|
|||
(while (not is-valid)
|
||||
(set move (get-move))
|
||||
(set is-valid (valid-move? move))
|
||||
(let [idx (index-of-move move)]
|
||||
(if (not is-valid)
|
||||
(print "Try again.")
|
||||
(do
|
||||
(print (.. "You chose " move))
|
||||
(tset moves idx game.player)
|
||||
(game:update idx)
|
||||
)
|
||||
(game:update move)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue