feat: add flying phase
parent
91b1662302
commit
1250f9f057
|
@ -0,0 +1,2 @@
|
||||||
|
# musings and learnings
|
||||||
|
2024-06-14T19:42:55-06:00 i sure do miss types sometimes. i'd like a little warning to pop up and warn me that i'm returning a string, e.g., when i ought to be returning a bool. i wonder if i could use some kind of a fake struct.. it would just a table, obviously. but maybe i could write a function that registers structs with a struct registry? and then another function to wrap functions that take a struct as a parameter to enforce the shape of the struct?
|
|
@ -0,0 +1,201 @@
|
||||||
|
---
|
||||||
|
title: TILDE30
|
||||||
|
subtitle: nine mens morris fennel game
|
||||||
|
author: dozens
|
||||||
|
created: 2024-06-01
|
||||||
|
updated: 2024-06-14
|
||||||
|
---
|
||||||
|
.pl 999i
|
||||||
|
.ce
|
||||||
|
{{title}}
|
||||||
|
|
||||||
|
.ce
|
||||||
|
{{subtitle}}
|
||||||
|
|
||||||
|
.IP 01
|
||||||
|
what is up tilde30 fans it's ya boi dozens back with another up date on my
|
||||||
|
project! today i found a bug that was preventing mills from being recognized
|
||||||
|
as mills. my algorithm for detecting mills is kind of (probably needlessly?)
|
||||||
|
complex. but luckily, i had already written a small module for writing unit
|
||||||
|
tests. so after a small refactor to isolate the individual steps as functions
|
||||||
|
that i can export, i imported them into a test file and was able to more
|
||||||
|
carefully examine each step. turns out the culprit was a small reducer in
|
||||||
|
which i was doing an 'and' when i ought to have been doing an 'or'. literally
|
||||||
|
just changed one word and that fixed it. but i'm still pleased with the
|
||||||
|
process by which i arrived at that realization. i'm now confident that the
|
||||||
|
entirety of the mill detection algorithm does what i want it to do. yay unit
|
||||||
|
tests!
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 02
|
||||||
|
today's goal was to implement capturing. but instead i discovered a bug in
|
||||||
|
rendering the board, and fixed that. i didn't bother taking the time to
|
||||||
|
really understand why the bug was happening. but after rewriting (and
|
||||||
|
simplifying) the render function, it's working correctly now. so yippee for
|
||||||
|
that. up next: capturing!
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 03
|
||||||
|
went to go do some remote smol computering. posted up at the library and fixed
|
||||||
|
a small typo that was preventing the game state from advancing from phase 1
|
||||||
|
to phase 2. and that's about all i could stand doing whilst coding on my
|
||||||
|
phone. later, on my laptop, successfully implemented capturing. next
|
||||||
|
up: prevent a player from capturing a checker that is part of a mill. i think
|
||||||
|
this will lead to a refactoring of the 'mill?' algorithm to generalize it a
|
||||||
|
little more.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 04
|
||||||
|
i did not work on tidle30 today. not enough spoons.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 05
|
||||||
|
today i did the refactor of the 'mill?' algorithm.
|
||||||
|
i ended up making it much more simple than how i had originally written it.
|
||||||
|
and it works pretty great!
|
||||||
|
the goal of this refactor was to be able check for a mill
|
||||||
|
after a move in order to change the game phase from placing to capture,
|
||||||
|
and also to check for a mill before a move
|
||||||
|
in order to check whether a capture is legal.
|
||||||
|
(a capture cannot break up a mill.)
|
||||||
|
as predicted,
|
||||||
|
my test-driven development workflow
|
||||||
|
made the refactor pretty painless.
|
||||||
|
i just got tripped up for a while
|
||||||
|
because i didn't realize i was passing the wrong type of move to the function.
|
||||||
|
you see, i have two different representations of a move:
|
||||||
|
one is a number 1 - 24 referring to the index of an array of player moves
|
||||||
|
(that is, the game state is a single array with values 0 = vacant, 1 = occupied by player 1, 2 = player 2);
|
||||||
|
and the other is an alphanumeric value (e.g. A1, b4, 7G)
|
||||||
|
referring to a place on the game board.
|
||||||
|
i frequently have to convert e.g. B2 into 4.
|
||||||
|
and what i don't really have right now
|
||||||
|
is a good type system that can tell me if i passed the wrong type to a function.
|
||||||
|
oh well!
|
||||||
|
i guess i'll have to do some manual type checking
|
||||||
|
in each function if i really want that kind of type safety.
|
||||||
|
this completes milestone 3: capture a checker.
|
||||||
|
next up:
|
||||||
|
implement some kind of a play counter
|
||||||
|
so the game can transition from placing to sliding.
|
||||||
|
.nf
|
||||||
|
http://cgit.tilde.town/~dozens/9mm/commit/?id=7776b2011a2585723078b275c838fd7332488d76
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 06
|
||||||
|
added transitioning from Placing to Moving,
|
||||||
|
and also implemented Moving logic.
|
||||||
|
this completes milestones 4 and 5.
|
||||||
|
starting to feel like a real game!
|
||||||
|
after getting my ass beat yesterday
|
||||||
|
by unknown function parameter types,
|
||||||
|
i added some type assertions to my new function.
|
||||||
|
and sure enough,
|
||||||
|
later on i passed the wrong value to the function.
|
||||||
|
but this time the assertion failed
|
||||||
|
and gave me a useful error message.
|
||||||
|
wahoooooo!
|
||||||
|
no time wasted tonight haha.
|
||||||
|
up next:
|
||||||
|
fix a bug that prevents captures from happening
|
||||||
|
during the moving phase?
|
||||||
|
watching:
|
||||||
|
maniac (2018) on netflix,
|
||||||
|
starring emma stone and jonah hill.
|
||||||
|
.nf
|
||||||
|
http://cgit.tilde.town/~dozens/9mm/commit/?id=f985dc4e5c9fdec06436c21440c3dc7245369847
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 07
|
||||||
|
as i said earlier,
|
||||||
|
there is currently a bug
|
||||||
|
that is preventing the board
|
||||||
|
from updating when a capture happens.
|
||||||
|
instead of working on the bug,
|
||||||
|
i instead focused on how sad it makes me
|
||||||
|
to have to enter 18 - 20 moves every time
|
||||||
|
just to test the capturing ui in the moving phase of the game.
|
||||||
|
this sadness inspired me to write an expect(1) script
|
||||||
|
that will interact with the game ui
|
||||||
|
and make all the moves for me.
|
||||||
|
much faster!
|
||||||
|
then i abstracted and isolated the moves
|
||||||
|
into a data file
|
||||||
|
so that for future ui testing,
|
||||||
|
i can just write down a list of moves
|
||||||
|
and not write a whole expect script.
|
||||||
|
and then i wrote a small awk script
|
||||||
|
that will convert data files into expect files.
|
||||||
|
so now i have some basic ui scripting,
|
||||||
|
which is maybe the first step toward actual ui testing?
|
||||||
|
and i have already used it to confirm the behavior
|
||||||
|
in both the placing phase
|
||||||
|
and the moving phase.
|
||||||
|
so it's not anything unique to phase 2
|
||||||
|
despite my original suspicions.
|
||||||
|
thanks, tests!
|
||||||
|
up next:
|
||||||
|
fix the dang bug
|
||||||
|
.nf
|
||||||
|
http://cgit.tilde.town/~dozens/9mm/commit/?id=91b1662302c14cf84ca8b90c1f3ec20a585f67a5
|
||||||
|
.fi
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 08
|
||||||
|
with fresh eyes,
|
||||||
|
i was able to see the bug that was preventing capturing.
|
||||||
|
it was a single line in the update function.
|
||||||
|
and i deleted it!
|
||||||
|
then i set about trying to allow transitioning
|
||||||
|
moving to flying.
|
||||||
|
but i introduced another bug
|
||||||
|
that i can't find right now..
|
||||||
|
it prevents capturing in the moving phase.
|
||||||
|
i'll have to look at it more later.
|
||||||
|
right now,
|
||||||
|
i have to finish packing!
|
||||||
|
i'm going on vacation!
|
||||||
|
we'll see whether or not i'm able to continue
|
||||||
|
working on 9mm / tilde30
|
||||||
|
while away from home.
|
||||||
|
i'm not bringing my laptop.
|
||||||
|
so i'll be limited to coding on my phone
|
||||||
|
with my little folding bluetooth keyboard.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP "WEEK ONE REVIEW"
|
||||||
|
when i look back on week one,
|
||||||
|
i feel like i made more progress
|
||||||
|
than i had expected to.
|
||||||
|
even while spending time on writing unit tests
|
||||||
|
and "ui tests."
|
||||||
|
if i keep up this pace
|
||||||
|
then i expect i'll be done with the game in another week.
|
||||||
|
but i'm on vacation next week,
|
||||||
|
so i'm not confident that i will keep up the pace.
|
||||||
|
whether or not i get around to it as part of tilde30,
|
||||||
|
i do want to build a gui frontend for the game.
|
||||||
|
because i think that would be fun.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP "09-13"
|
||||||
|
i did not do any computering
|
||||||
|
during this five day low-tech beach vacation.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.IP 14
|
||||||
|
fixed the bug that prevented capturing during the moving phase.
|
||||||
|
implemented flying!
|
||||||
|
and also handled an edge case
|
||||||
|
where you cannot break up a mill
|
||||||
|
when capturing
|
||||||
|
unless there are no other non-milled checkers.
|
||||||
|
in which case
|
||||||
|
you can break up a mill
|
||||||
|
when capturing.
|
||||||
|
up next:
|
||||||
|
ending the game.
|
||||||
|
|
||||||
|
.pl \n[nl]u
|
4
justfile
4
justfile
|
@ -9,3 +9,7 @@ test:
|
||||||
# build expect scripts
|
# build expect scripts
|
||||||
expects:
|
expects:
|
||||||
for f in test/*.dat; do awk -f test/test.awk $f > ${f/dat/expect}; done
|
for f in test/*.dat; do awk -f test/test.awk $f > ${f/dat/expect}; done
|
||||||
|
|
||||||
|
# make the project
|
||||||
|
project:
|
||||||
|
awk '$0 ~ /^---$/ && times++ < 2 { a=!a;next; } a' doc/tilde30.t | recfmt -f doc/tilde30.t | awk '$0 ~ /^---$/ { times++;next } times > 1' | nroff -ms -Tascii
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
(local {: mill-at? } (require :lib.mill))
|
||||||
|
(local {: mills } (require :lib.constants))
|
||||||
|
|
||||||
|
(fn toggle-player [p] (if (= p 1) 2 1))
|
||||||
|
|
||||||
|
(fn only-player-moves [moves player]
|
||||||
|
(icollect [_ move (ipairs moves)] (if (= move player) player 0)))
|
||||||
|
|
||||||
|
(fn all-moves-are-mills? [moves player]
|
||||||
|
(accumulate [result true
|
||||||
|
i m (ipairs moves) ]
|
||||||
|
(and result (if (= m 0) true (mill-at? mills moves i)))))
|
||||||
|
|
||||||
|
(fn all-mills? [all-moves current-player]
|
||||||
|
(let [next-player (toggle-player current-player)
|
||||||
|
player-moves (only-player-moves all-moves next-player)
|
||||||
|
all-mills (all-moves-are-mills? player-moves current-player)]
|
||||||
|
all-mills))
|
||||||
|
|
||||||
|
{: all-mills?
|
||||||
|
;; do not use; just for testing:
|
||||||
|
: toggle-player
|
||||||
|
: only-player-moves
|
||||||
|
: all-moves-are-mills?
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
(let [{: describe
|
||||||
|
:end test-end} (require :lib.test)
|
||||||
|
{: all-mills?
|
||||||
|
: toggle-player
|
||||||
|
: only-player-moves
|
||||||
|
: all-moves-are-mills?
|
||||||
|
} (require :lib.all-mills)]
|
||||||
|
|
||||||
|
(describe "all-mills" (fn []
|
||||||
|
(describe "#toggle-player()" (fn [t]
|
||||||
|
(t {:given "a player"
|
||||||
|
:should "return the next"
|
||||||
|
:expected 2
|
||||||
|
:actual (toggle-player 1)
|
||||||
|
})))
|
||||||
|
(describe "#only-player-moves()" (fn [t]
|
||||||
|
(let [moves [ 0 2 0 2 2 2 0 0 0 0 0 0 0 2 0 0 0 2 0 2 0 1 1 1 ]
|
||||||
|
expected [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 ]
|
||||||
|
]
|
||||||
|
(t {:given "a bunch of moves and a player"
|
||||||
|
:should "filter out all the moves not belonging to the player"
|
||||||
|
: expected
|
||||||
|
:actual (only-player-moves moves 1)
|
||||||
|
}))))
|
||||||
|
(describe "#all-moves-are-mills?()" (fn [t]
|
||||||
|
(let [moves [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 ]
|
||||||
|
]
|
||||||
|
(t {:given "a bunch of moves and a player"
|
||||||
|
:should "return true if all the player moves are mills"
|
||||||
|
:expected true
|
||||||
|
:actual (all-moves-are-mills? moves 1)
|
||||||
|
}))
|
||||||
|
(let [moves [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 ]
|
||||||
|
]
|
||||||
|
(t {:given "a bunch of moves and no mill and a player"
|
||||||
|
:should "return false"
|
||||||
|
:expected false
|
||||||
|
:actual (all-moves-are-mills? moves 1)
|
||||||
|
}))))
|
||||||
|
(test-end))))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
(local {: all-mills?} (require :lib.all-mills))
|
||||||
(local {: contains} (require :lib.contains))
|
(local {: contains} (require :lib.contains))
|
||||||
(local {: head} (require :lib.head))
|
(local {: head} (require :lib.head))
|
||||||
(local {: keys} (require :lib.keys))
|
(local {: keys} (require :lib.keys))
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
(local {: tail} (require :lib.tail))
|
(local {: tail} (require :lib.tail))
|
||||||
|
|
||||||
{
|
{
|
||||||
|
: all-mills?
|
||||||
: contains
|
: contains
|
||||||
: head
|
: head
|
||||||
: keys
|
: keys
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
(let [candidates (get-candidates all-mills move)
|
(let [candidates (get-candidates all-mills move)
|
||||||
my-moves (candidate-moves candidates current-moves)
|
my-moves (candidate-moves candidates current-moves)
|
||||||
my-mills (move-mills my-moves)
|
my-mills (move-mills my-moves)
|
||||||
result (any my-mills)
|
result (any my-mills)]
|
||||||
]
|
|
||||||
result))
|
result))
|
||||||
|
|
||||||
{: mill-at?
|
{: mill-at?
|
||||||
|
|
104
main.fnl
104
main.fnl
|
@ -5,6 +5,7 @@
|
||||||
: kvflip
|
: kvflip
|
||||||
: pprint
|
: pprint
|
||||||
: slice
|
: slice
|
||||||
|
: all-mills?
|
||||||
:mill-at? mill-at-maker
|
:mill-at? mill-at-maker
|
||||||
:space-is-neighbor? space-is-neighbor-maker
|
:space-is-neighbor? space-is-neighbor-maker
|
||||||
} (require :lib.index))
|
} (require :lib.index))
|
||||||
|
@ -28,16 +29,18 @@
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
;; story mode:
|
||||||
;; there are two players
|
;; there are two players
|
||||||
;; their names are LUIGI and MARIO
|
;; their names are WIGI and MALO
|
||||||
(local player {
|
(local player {
|
||||||
:one 1 ;; luigi has light cows
|
:one 1 ;; wigi has light cows
|
||||||
:two 2 ;; mario has DARK cows >:)
|
:two 2 ;; malo has DARK cows >:)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
; return the numerical index (1-24) of a [A-Za-z0-9] formatted move
|
; return the numerical index (1-24) of a [A-Za-z0-9] formatted move
|
||||||
(fn index-of-move [m]
|
(fn index-of-move [m]
|
||||||
|
(assert (= "string" (type m)) "index-of-move needs a string argument")
|
||||||
(let [upper (string.upper m)
|
(let [upper (string.upper m)
|
||||||
rev (string.reverse upper)
|
rev (string.reverse upper)
|
||||||
idx (head (icollect [i v (ipairs const.spaces)]
|
idx (head (icollect [i v (ipairs const.spaces)]
|
||||||
|
@ -45,40 +48,69 @@
|
||||||
idx))
|
idx))
|
||||||
|
|
||||||
|
|
||||||
|
(fn player-count [moves player]
|
||||||
|
(accumulate [count 0
|
||||||
|
_ x (ipairs moves)]
|
||||||
|
(if (= x player) (+ count 1) count)))
|
||||||
|
|
||||||
|
|
||||||
;; game state object
|
;; game state object
|
||||||
(local game {
|
(local game {
|
||||||
:player player.one
|
:player player.one
|
||||||
:stage stages.placing
|
:stage stages.placing
|
||||||
:update (fn [self move]
|
:update (fn [self move]
|
||||||
(case self.stage
|
(case self.stage
|
||||||
4 ;; capture
|
4 ;; CAPTURE
|
||||||
(do
|
(do
|
||||||
;; TODO: capturing during moving is not working?
|
|
||||||
(tset self.moves (index-of-move move) 0)
|
(tset self.moves (index-of-move move) 0)
|
||||||
(tset self :player (self:next-player))
|
(tset self :player (self:next-player))
|
||||||
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
(let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
|
||||||
(tset self.moves (index-of-move move) self.player)
|
movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))]
|
||||||
)
|
(tset self :stage (if flytime stages.flying
|
||||||
1 ;; placing
|
movetime stages.moving
|
||||||
|
stages.placing))))
|
||||||
|
1 ;; PLACING
|
||||||
(do
|
(do
|
||||||
(set self.pieces-placed (+ 1 self.pieces-placed))
|
(set self.pieces-placed (+ 1 self.pieces-placed))
|
||||||
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
||||||
(tset self.moves (index-of-move move) self.player)
|
(tset self.moves (index-of-move move) self.player)
|
||||||
(if (mill-at? self.moves (index-of-move move))
|
(let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
|
||||||
(tset self :stage stages.capture)
|
movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))
|
||||||
(tset self :player (self:next-player))
|
capturetime (mill-at? self.moves (index-of-move move))]
|
||||||
)
|
(tset self :stage (if
|
||||||
)
|
capturetime stages.capture
|
||||||
2 ;; moving
|
flytime stages.flying
|
||||||
|
movetime stages.moving
|
||||||
|
stages.placing))
|
||||||
|
(if (not capturetime) (tset self :player (self:next-player)))))
|
||||||
|
2 ;; MOVING
|
||||||
(let [from (index-of-move (string.sub move 1 2))
|
(let [from (index-of-move (string.sub move 1 2))
|
||||||
to (index-of-move (string.sub move -2 -1))]
|
to (index-of-move (string.sub move -2 -1))]
|
||||||
(tset self.moves from 0)
|
(tset self.moves from 0)
|
||||||
(tset self.moves to self.player)
|
(tset self.moves to self.player)
|
||||||
(if (mill-at? self.moves to)
|
(let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves (self:next-player))))
|
||||||
(tset self :stage stages.capture)
|
movetime (and (> self.pieces-placed 17) (> (player-count self.moves (self:next-player)) 3))
|
||||||
(tset self :player (self:next-player))
|
capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))]
|
||||||
)
|
(tset self :stage (if
|
||||||
)
|
capturetime stages.capture
|
||||||
|
flytime stages.flying
|
||||||
|
movetime stages.moving
|
||||||
|
stages.placing))
|
||||||
|
(if (not capturetime) (tset self :player (self:next-player)))))
|
||||||
|
3 ;; FLYING
|
||||||
|
(let [from (index-of-move (string.sub move 1 2))
|
||||||
|
to (index-of-move (string.sub move -2 -1))]
|
||||||
|
(tset self.moves from 0)
|
||||||
|
(tset self.moves to self.player)
|
||||||
|
(let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves (self:next-player))))
|
||||||
|
movetime (and (> self.pieces-placed 17) (> (player-count self.moves (self:next-player)) 3))
|
||||||
|
capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))]
|
||||||
|
(tset self :stage (if
|
||||||
|
capturetime stages.capture
|
||||||
|
flytime stages.flying
|
||||||
|
movetime stages.moving
|
||||||
|
stages.placing))
|
||||||
|
(if (not capturetime) (tset self :player (self:next-player)))))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
:next-player (fn [self] (if (= player.one self.player) player.two player.one))
|
:next-player (fn [self] (if (= player.one self.player) player.two player.one))
|
||||||
|
@ -98,6 +130,7 @@
|
||||||
(game:init)
|
(game:init)
|
||||||
|
|
||||||
|
|
||||||
|
; TODO: move to lib utility
|
||||||
(fn string-upper [s]
|
(fn string-upper [s]
|
||||||
(.. (string.upper (string.sub s 1 1)) (string.sub s 2)))
|
(.. (string.upper (string.sub s 1 1)) (string.sub s 2)))
|
||||||
|
|
||||||
|
@ -136,27 +169,23 @@
|
||||||
(let [unoccupied? 0] ; i.e. is move equal to 0
|
(let [unoccupied? 0] ; i.e. is move equal to 0
|
||||||
(= unoccupied? (. game.moves (index-of-move m)))))
|
(= unoccupied? (. game.moves (index-of-move m)))))
|
||||||
|
|
||||||
|
; is the space m occupied by the player's opponent?
|
||||||
(fn space-is-occupied-by-opponent? [m]
|
(fn space-is-occupied-by-opponent? [m]
|
||||||
|
"is the space m occupied by the player's opponent?"
|
||||||
(let [opponent (if (= game.player 1) 2 1)
|
(let [opponent (if (= game.player 1) 2 1)
|
||||||
result (= opponent (. game.moves (index-of-move m))) ]
|
result (= opponent (. game.moves (index-of-move m))) ]
|
||||||
result))
|
result))
|
||||||
|
|
||||||
|
; checks that the first 2 charcters and the last 2 characters
|
||||||
|
; of a string are legal spaces
|
||||||
|
; moving-format is the same as flying-format
|
||||||
(fn moving-format? [m]
|
(fn moving-format? [m]
|
||||||
(let [from (string.sub m 1 2)
|
(let [from (string.sub m 1 2)
|
||||||
to (string.sub m -2 -1)]
|
to (string.sub m -2 -1)]
|
||||||
(and (space-exists? from) (space-exists? to))))
|
(and (>= (length m) 4) (space-exists? from) (space-exists? to))))
|
||||||
|
|
||||||
|
|
||||||
; is this a legal move?
|
; is this a legal move?
|
||||||
; 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..
|
|
||||||
; https://fennel-lang.org/reference#case-try-for-matching-multiple-steps
|
|
||||||
; update: i didn't really like that
|
|
||||||
; i think maybe i do want the monad after all..
|
|
||||||
; i'll come back to it later
|
|
||||||
(fn valid-move? [move]
|
(fn valid-move? [move]
|
||||||
(or
|
(or
|
||||||
(and
|
(and
|
||||||
|
@ -169,8 +198,10 @@
|
||||||
(= stages.capture game.stage)
|
(= stages.capture game.stage)
|
||||||
(or (space-is-occupied-by-opponent? move)
|
(or (space-is-occupied-by-opponent? move)
|
||||||
(print "Choose an opponent's piece to remove."))
|
(print "Choose an opponent's piece to remove."))
|
||||||
(or (not (mill-at? game.moves (index-of-move move)))
|
(or (or (all-mills? game.moves game.player)
|
||||||
(print "Ma'am, it is ILLEGAL to break up a mill."))
|
(not (mill-at? game.moves (index-of-move move))))
|
||||||
|
(print "Ma'am, it is ILLEGAL to break up a mill.")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
(and
|
(and
|
||||||
(= stages.moving game.stage)
|
(= stages.moving game.stage)
|
||||||
|
@ -184,8 +215,13 @@
|
||||||
(print "That ain't your neighbor, Johnny"))
|
(print "That ain't your neighbor, Johnny"))
|
||||||
)
|
)
|
||||||
(and
|
(and
|
||||||
;; TODO: add flying phase
|
|
||||||
(= stages.flying game.stage)
|
(= stages.flying 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!"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
# PLACING PHASE (18 moves)
|
||||||
|
A1
|
||||||
|
A4
|
||||||
|
A7
|
||||||
|
b2
|
||||||
|
b4
|
||||||
|
b6
|
||||||
|
c3
|
||||||
|
c4
|
||||||
|
c5
|
||||||
|
d1
|
||||||
|
d2
|
||||||
|
d3
|
||||||
|
d5
|
||||||
|
d6
|
||||||
|
d7
|
||||||
|
e3
|
||||||
|
e4
|
||||||
|
e5
|
||||||
|
# MOVING PHASE (6 captures)
|
||||||
|
e4f4
|
||||||
|
e3e4
|
||||||
|
f4g4
|
||||||
|
d3e3
|
||||||
|
b4
|
||||||
|
g4g1
|
||||||
|
c4b4
|
||||||
|
d2
|
||||||
|
d7g7
|
||||||
|
e3d3
|
||||||
|
c5c4
|
||||||
|
b2d2
|
||||||
|
a1
|
||||||
|
c4c5
|
||||||
|
d2b2
|
||||||
|
d5
|
||||||
|
g7g4
|
||||||
|
b2d2
|
||||||
|
a7
|
||||||
|
c3c4
|
||||||
|
d2b2
|
||||||
|
c4
|
||||||
|
# FLYING PHASE!
|
||||||
|
c5g7
|
||||||
|
d3
|
||||||
|
e4f4
|
||||||
|
g7f6
|
||||||
|
b2d2
|
||||||
|
f6g7
|
||||||
|
d1
|
||||||
|
d2b2
|
|
@ -0,0 +1,43 @@
|
||||||
|
# PLACING PHASE (18 moves)
|
||||||
|
A1
|
||||||
|
A4
|
||||||
|
A7
|
||||||
|
b2
|
||||||
|
b4
|
||||||
|
b6
|
||||||
|
c3
|
||||||
|
c4
|
||||||
|
c5
|
||||||
|
d1
|
||||||
|
d2
|
||||||
|
d3
|
||||||
|
d5
|
||||||
|
d6
|
||||||
|
d7
|
||||||
|
e3
|
||||||
|
e4
|
||||||
|
e5
|
||||||
|
# MOVING PHASE (6 captures)
|
||||||
|
e4f4
|
||||||
|
e3e4
|
||||||
|
f4g4
|
||||||
|
d3e3
|
||||||
|
b4
|
||||||
|
g4g1
|
||||||
|
c4b4
|
||||||
|
d2
|
||||||
|
d7g7
|
||||||
|
e3d3
|
||||||
|
c5c4
|
||||||
|
b2d2
|
||||||
|
a1
|
||||||
|
c4c5
|
||||||
|
d2b2
|
||||||
|
d5
|
||||||
|
g7g4
|
||||||
|
b2d2
|
||||||
|
a7
|
||||||
|
c3c4
|
||||||
|
d2b2
|
||||||
|
c4
|
||||||
|
# FLYING PHASE!
|
|
@ -1,3 +1,4 @@
|
||||||
|
# placing time
|
||||||
A1
|
A1
|
||||||
A4
|
A4
|
||||||
A7
|
A7
|
||||||
|
@ -16,7 +17,9 @@ d7
|
||||||
e3
|
e3
|
||||||
e4
|
e4
|
||||||
e5
|
e5
|
||||||
|
# moving time
|
||||||
e4f4
|
e4f4
|
||||||
e3e4
|
e3e4
|
||||||
f4g4
|
f4g4
|
||||||
|
# player 2 to capture:
|
||||||
d3e3
|
d3e3
|
||||||
|
|
|
@ -3,6 +3,8 @@ BEGIN {
|
||||||
print "spawn fennel main.fnl"
|
print "spawn fennel main.fnl"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/^#/ { next }
|
||||||
|
|
||||||
{ print "expect -re \"Player .'s turn:\""
|
{ print "expect -re \"Player .'s turn:\""
|
||||||
print "send -- \"" $0 "\\r\""
|
print "send -- \"" $0 "\\r\""
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue