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
|
||||
expects:
|
||||
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 {: head} (require :lib.head))
|
||||
(local {: keys} (require :lib.keys))
|
||||
|
@ -9,6 +10,7 @@
|
|||
(local {: tail} (require :lib.tail))
|
||||
|
||||
{
|
||||
: all-mills?
|
||||
: contains
|
||||
: head
|
||||
: keys
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
(let [candidates (get-candidates all-mills move)
|
||||
my-moves (candidate-moves candidates current-moves)
|
||||
my-mills (move-mills my-moves)
|
||||
result (any my-mills)
|
||||
]
|
||||
result (any my-mills)]
|
||||
result))
|
||||
|
||||
{: mill-at?
|
||||
|
|
104
main.fnl
104
main.fnl
|
@ -5,6 +5,7 @@
|
|||
: kvflip
|
||||
: pprint
|
||||
: slice
|
||||
: all-mills?
|
||||
:mill-at? mill-at-maker
|
||||
:space-is-neighbor? space-is-neighbor-maker
|
||||
} (require :lib.index))
|
||||
|
@ -28,16 +29,18 @@
|
|||
})
|
||||
|
||||
|
||||
;; story mode:
|
||||
;; there are two players
|
||||
;; their names are LUIGI and MARIO
|
||||
;; their names are WIGI and MALO
|
||||
(local player {
|
||||
:one 1 ;; luigi has light cows
|
||||
:two 2 ;; mario has DARK cows >:)
|
||||
:one 1 ;; wigi has light cows
|
||||
:two 2 ;; malo has DARK cows >:)
|
||||
})
|
||||
|
||||
|
||||
; return the numerical index (1-24) of a [A-Za-z0-9] formatted move
|
||||
(fn index-of-move [m]
|
||||
(assert (= "string" (type m)) "index-of-move needs a string argument")
|
||||
(let [upper (string.upper m)
|
||||
rev (string.reverse upper)
|
||||
idx (head (icollect [i v (ipairs const.spaces)]
|
||||
|
@ -45,40 +48,69 @@
|
|||
idx))
|
||||
|
||||
|
||||
(fn player-count [moves player]
|
||||
(accumulate [count 0
|
||||
_ x (ipairs moves)]
|
||||
(if (= x player) (+ count 1) count)))
|
||||
|
||||
|
||||
;; game state object
|
||||
(local game {
|
||||
:player player.one
|
||||
:stage stages.placing
|
||||
:update (fn [self move]
|
||||
(case self.stage
|
||||
4 ;; capture
|
||||
4 ;; CAPTURE
|
||||
(do
|
||||
;; TODO: capturing during moving is not working?
|
||||
(tset self.moves (index-of-move move) 0)
|
||||
(tset self :player (self:next-player))
|
||||
(tset self :stage (if (> self.pieces-placed 17) stages.moving stages.placing))
|
||||
(tset self.moves (index-of-move move) self.player)
|
||||
)
|
||||
1 ;; placing
|
||||
(let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
|
||||
movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))]
|
||||
(tset self :stage (if flytime stages.flying
|
||||
movetime stages.moving
|
||||
stages.placing))))
|
||||
1 ;; PLACING
|
||||
(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 [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
|
||||
movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))
|
||||
capturetime (mill-at? self.moves (index-of-move move))]
|
||||
(tset self :stage (if
|
||||
capturetime stages.capture
|
||||
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))
|
||||
to (index-of-move (string.sub move -2 -1))]
|
||||
(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))
|
||||
)
|
||||
)
|
||||
(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)))))
|
||||
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))
|
||||
|
@ -98,6 +130,7 @@
|
|||
(game:init)
|
||||
|
||||
|
||||
; TODO: move to lib utility
|
||||
(fn string-upper [s]
|
||||
(.. (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
|
||||
(= unoccupied? (. game.moves (index-of-move m)))))
|
||||
|
||||
|
||||
; is the space m occupied by the player's opponent?
|
||||
(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)
|
||||
result (= opponent (. game.moves (index-of-move m))) ]
|
||||
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]
|
||||
(let [from (string.sub m 1 2)
|
||||
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?
|
||||
; 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]
|
||||
(or
|
||||
(and
|
||||
|
@ -169,8 +198,10 @@
|
|||
(= stages.capture game.stage)
|
||||
(or (space-is-occupied-by-opponent? move)
|
||||
(print "Choose an opponent's piece to remove."))
|
||||
(or (not (mill-at? game.moves (index-of-move move)))
|
||||
(print "Ma'am, it is ILLEGAL to break up a mill."))
|
||||
(or (or (all-mills? game.moves game.player)
|
||||
(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)
|
||||
|
@ -184,8 +215,13 @@
|
|||
(print "That ain't your neighbor, Johnny"))
|
||||
)
|
||||
(and
|
||||
;; TODO: add flying phase
|
||||
(= 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
|
||||
A4
|
||||
A7
|
||||
|
@ -16,7 +17,9 @@ d7
|
|||
e3
|
||||
e4
|
||||
e5
|
||||
# moving time
|
||||
e4f4
|
||||
e3e4
|
||||
f4g4
|
||||
# player 2 to capture:
|
||||
d3e3
|
||||
|
|
|
@ -3,6 +3,8 @@ BEGIN {
|
|||
print "spawn fennel main.fnl"
|
||||
}
|
||||
|
||||
/^#/ { next }
|
||||
|
||||
{ print "expect -re \"Player .'s turn:\""
|
||||
print "send -- \"" $0 "\\r\""
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue