feat: add end game
This commit is contained in:
		
							parent
							
								
									1250f9f057
								
							
						
					
					
						commit
						ce09973e7c
					
				@ -197,5 +197,55 @@ you can break up a mill
 | 
				
			|||||||
when capturing.
 | 
					when capturing.
 | 
				
			||||||
up next:
 | 
					up next:
 | 
				
			||||||
ending the game.
 | 
					ending the game.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP 15
 | 
				
			||||||
 | 
					implemented a game ending!
 | 
				
			||||||
 | 
					now if a player has fewer than 3 checkers,
 | 
				
			||||||
 | 
					the other player wins the game.
 | 
				
			||||||
 | 
					up next:
 | 
				
			||||||
 | 
					endgame edge case where if a player has 3 or more checkers,
 | 
				
			||||||
 | 
					but no available legal moves,
 | 
				
			||||||
 | 
					then they lose.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP "WEEK TWO REVIEW"
 | 
				
			||||||
 | 
					light week.
 | 
				
			||||||
 | 
					spent almost all of it on vacation
 | 
				
			||||||
 | 
					and not working on tilde30 at all.
 | 
				
			||||||
 | 
					nonetheless,
 | 
				
			||||||
 | 
					i'm mostly finished with the core of the game.
 | 
				
			||||||
 | 
					i have one small edge case to iron out
 | 
				
			||||||
 | 
					and then the game will be all the way complete.
 | 
				
			||||||
 | 
					i think for my first stretch goal,
 | 
				
			||||||
 | 
					i want to add some kind of generative story mode
 | 
				
			||||||
 | 
					based on player moves and decisions, etc.
 | 
				
			||||||
 | 
					so that by the time you're done with the game,
 | 
				
			||||||
 | 
					you have a unique little story to take with you.
 | 
				
			||||||
 | 
					i'm not sure whether i want to do a tracery grammar
 | 
				
			||||||
 | 
					type of thing..
 | 
				
			||||||
 | 
					could be fun to try to write that.
 | 
				
			||||||
 | 
					well here's to tilde30 being half over!
 | 
				
			||||||
 | 
					hope everybody is having fun making progress
 | 
				
			||||||
 | 
					on your projects!
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP 16
 | 
				
			||||||
 | 
					I wrote the "no-moves?" function
 | 
				
			||||||
 | 
					to determine whether a player has no legal moves remaining.
 | 
				
			||||||
 | 
					And a test for it.
 | 
				
			||||||
 | 
					But integrating it created a bug I need to track down.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP 17
 | 
				
			||||||
 | 
					Didn't fix the bug,
 | 
				
			||||||
 | 
					but rewrote "no-moves?"
 | 
				
			||||||
 | 
					using the "->" threading macro
 | 
				
			||||||
 | 
					which is neat.
 | 
				
			||||||
 | 
					I've never used it before.
 | 
				
			||||||
 | 
					It's basically the "compose" or "pipe" function
 | 
				
			||||||
 | 
					that I have enjoyed using before in javascript.
 | 
				
			||||||
 | 
					up next: fix that bug!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pl \n[nl]u
 | 
					.pl \n[nl]u
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +1,15 @@
 | 
				
			|||||||
 | 
					(local str (require :lib.string))
 | 
				
			||||||
 | 
					(local tbl (require :lib.table))
 | 
				
			||||||
(local {: all-mills?} (require :lib.all-mills))
 | 
					(local {: all-mills?} (require :lib.all-mills))
 | 
				
			||||||
(local {: contains} (require :lib.contains))
 | 
					 | 
				
			||||||
(local {: head} (require :lib.head))
 | 
					 | 
				
			||||||
(local {: keys} (require :lib.keys))
 | 
					 | 
				
			||||||
(local {: kvflip} (require :lib.kvflip))
 | 
					 | 
				
			||||||
(local {: mill-at?} (require :lib.mill))
 | 
					(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 {: space-is-neighbor?} (require :lib.space-is-neighbor))
 | 
				
			||||||
(local {: tail} (require :lib.tail))
 | 
					(local {: no-moves?} (require :lib.no-moves))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					 : str
 | 
				
			||||||
 | 
					 : tbl
 | 
				
			||||||
 : all-mills?
 | 
					 : all-mills?
 | 
				
			||||||
 : contains
 | 
					 | 
				
			||||||
 : head
 | 
					 | 
				
			||||||
 : keys
 | 
					 | 
				
			||||||
 : kvflip
 | 
					 | 
				
			||||||
 : mill-at?
 | 
					 : mill-at?
 | 
				
			||||||
 : pprint
 | 
					 : no-moves?
 | 
				
			||||||
 : slice
 | 
					 | 
				
			||||||
 : space-is-neighbor?
 | 
					 : space-is-neighbor?
 | 
				
			||||||
 : tail
 | 
					 | 
				
			||||||
 }
 | 
					 }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								lib/no-moves.fnl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/no-moves.fnl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					(local {: tail} (require :lib.tail))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(fn get-player-idxs [player moves]
 | 
				
			||||||
 | 
					  (icollect [i p (ipairs moves)] (when (= p player) i)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(fn idx-to-neighbors [idxs all-neighbors]
 | 
				
			||||||
 | 
					  (icollect [_ i (ipairs idxs)] (tail (. all-neighbors i))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(fn neighbor-is-occupied? [neighbors moves]
 | 
				
			||||||
 | 
					  (icollect [_ move (ipairs neighbors)]
 | 
				
			||||||
 | 
					    (icollect [_ neighbor (ipairs move)]
 | 
				
			||||||
 | 
					      (not= (. moves neighbor) 0))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(fn reduce-to-bool [xs]
 | 
				
			||||||
 | 
					  (accumulate [acc true
 | 
				
			||||||
 | 
					               _ x (ipairs xs)]
 | 
				
			||||||
 | 
					               (and x)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(fn no-moves? [neighbors all-moves player]
 | 
				
			||||||
 | 
					  (-> (get-player-idxs player all-moves)
 | 
				
			||||||
 | 
					    (idx-to-neighbors neighbors)
 | 
				
			||||||
 | 
					    (neighbor-is-occupied? all-moves)
 | 
				
			||||||
 | 
					    (reduce-to-bool)
 | 
				
			||||||
 | 
					    (reduce-to-bool)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{: no-moves? }
 | 
				
			||||||
							
								
								
									
										51
									
								
								lib/no-moves.test.fnl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								lib/no-moves.test.fnl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					(let [{: no-moves?} (require :lib.no-moves)
 | 
				
			||||||
 | 
					      {: neighbors} (require :lib.constants)
 | 
				
			||||||
 | 
					      {: describe :end test-end} (require :lib.test)
 | 
				
			||||||
 | 
					      with-neighbors (partial no-moves? neighbors)
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (describe "no-moves()" (fn [t]
 | 
				
			||||||
 | 
					    (let [moves [ 1 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 ]
 | 
				
			||||||
 | 
					          player 1
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        (t {:given "one move with no moves"
 | 
				
			||||||
 | 
					            :should "return true"
 | 
				
			||||||
 | 
					            :expected true
 | 
				
			||||||
 | 
					            :actual (with-neighbors moves player)
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					    (let [moves [ 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
 | 
				
			||||||
 | 
					          player 1
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        (t {:given "one move with one move"
 | 
				
			||||||
 | 
					            :should "return false"
 | 
				
			||||||
 | 
					            :expected false
 | 
				
			||||||
 | 
					            :actual (with-neighbors moves player)
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					    (let [moves [ 1 1 1 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 ]
 | 
				
			||||||
 | 
					          player 1
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        (t {:given "several moves with no moves"
 | 
				
			||||||
 | 
					            :should "return true"
 | 
				
			||||||
 | 
					            :expected true
 | 
				
			||||||
 | 
					            :actual (with-neighbors moves player)
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					    (let [moves [ 0 2 0 2 1 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
 | 
				
			||||||
 | 
					          player 1
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        (t {:given "four occupied neighbors"
 | 
				
			||||||
 | 
					            :should "return true"
 | 
				
			||||||
 | 
					            :expected true
 | 
				
			||||||
 | 
					            :actual (with-neighbors moves player)
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					    (let [moves [ 1 2 1 2 0 2 1 2 1 2 1 0 1 2 1 2 2 2 0 1 0 0 0 0 0 ]
 | 
				
			||||||
 | 
					          player 2
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        (t {:given "this turn that is giving me trouble"
 | 
				
			||||||
 | 
					            :should "return true"
 | 
				
			||||||
 | 
					            :expected true
 | 
				
			||||||
 | 
					            :actual (with-neighbors moves player)
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (test-end))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								lib/string.fnl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								lib/string.fnl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					(fn capitalize [s]
 | 
				
			||||||
 | 
					  (.. (string.upper (string.sub s 1 1)) (string.sub s 2)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{: capitalize}
 | 
				
			||||||
							
								
								
									
										17
									
								
								lib/table.fnl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/table.fnl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					(local {: contains} (require :lib.contains))
 | 
				
			||||||
 | 
					(local {: head} (require :lib.head))
 | 
				
			||||||
 | 
					(local {: keys} (require :lib.keys))
 | 
				
			||||||
 | 
					(local {:kvflip invert} (require :lib.kvflip))
 | 
				
			||||||
 | 
					(local {:pprint print} (require :lib.tableprint))
 | 
				
			||||||
 | 
					(local {: slice} (require :lib.slice))
 | 
				
			||||||
 | 
					(local {: tail} (require :lib.tail))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					 : contains
 | 
				
			||||||
 | 
					 : head
 | 
				
			||||||
 | 
					 : keys
 | 
				
			||||||
 | 
					 : invert
 | 
				
			||||||
 | 
					 : print
 | 
				
			||||||
 | 
					 : slice
 | 
				
			||||||
 | 
					 : tail
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
							
								
								
									
										86
									
								
								main.fnl
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								main.fnl
									
									
									
									
									
								
							@ -1,19 +1,18 @@
 | 
				
			|||||||
;; helper and utility functions
 | 
					;; helper and utility functions
 | 
				
			||||||
(local {
 | 
					(local {
 | 
				
			||||||
  : contains
 | 
					  : str
 | 
				
			||||||
  : head
 | 
					  : tbl
 | 
				
			||||||
  : kvflip
 | 
					 | 
				
			||||||
  : pprint
 | 
					 | 
				
			||||||
  : slice
 | 
					 | 
				
			||||||
  : all-mills?
 | 
					  : 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
 | 
				
			||||||
 | 
					  :no-moves? no-moves-maker
 | 
				
			||||||
  } (require :lib.index))
 | 
					  } (require :lib.index))
 | 
				
			||||||
;; constants...more like just strings
 | 
					;; constants...more like just strings
 | 
				
			||||||
(local const (require :lib.constants))
 | 
					(local const (require :lib.constants))
 | 
				
			||||||
;; front-loading with some partials
 | 
					;; front-loading with some partials
 | 
				
			||||||
(local mill-at? (partial mill-at-maker const.mills))
 | 
					(local mill-at? (partial mill-at-maker const.mills))
 | 
				
			||||||
(local space-is-neighbor? (partial space-is-neighbor-maker const.neighbors))
 | 
					(local space-is-neighbor? (partial space-is-neighbor-maker const.neighbors))
 | 
				
			||||||
 | 
					(local no-moves? (partial no-moves-maker const.neighbors))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;; there are three phases of play:
 | 
					;; there are three phases of play:
 | 
				
			||||||
@ -43,7 +42,7 @@
 | 
				
			|||||||
  (assert (= "string" (type m)) "index-of-move needs a string argument")
 | 
					  (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   (tbl.head (icollect [i v (ipairs const.spaces)]
 | 
				
			||||||
                      (if (or (= v upper) (= v rev)) i)))]
 | 
					                      (if (or (= v upper) (= v rev)) i)))]
 | 
				
			||||||
       idx))
 | 
					       idx))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -63,19 +62,24 @@
 | 
				
			|||||||
      4 ;; CAPTURE
 | 
					      4 ;; CAPTURE
 | 
				
			||||||
        (do
 | 
					        (do
 | 
				
			||||||
          (tset self.moves (index-of-move move) 0)
 | 
					          (tset self.moves (index-of-move move) 0)
 | 
				
			||||||
          (tset self :player (self:next-player))
 | 
					          (let [flytime (and (self:phase-two?) (= 3 (player-count self.moves (self:next-player))))
 | 
				
			||||||
          (let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
 | 
					                movetime (and (self:phase-two?) (> (player-count self.moves (self:next-player)) 3))
 | 
				
			||||||
                movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))]
 | 
					                endtime (and (self:phase-two?)
 | 
				
			||||||
            (tset self :stage (if flytime stages.flying
 | 
					                             (or (< (length (icollect [_ m (ipairs self.moves)] (if (= m 1) 1))) 3)
 | 
				
			||||||
 | 
					                                 (< (length (icollect [_ m (ipairs self.moves)] (if (= m 2) 2))) 3)))]
 | 
				
			||||||
 | 
					            (tset self :stage (if endtime stages.complete
 | 
				
			||||||
 | 
					                                flytime stages.flying
 | 
				
			||||||
                                movetime stages.moving
 | 
					                                movetime stages.moving
 | 
				
			||||||
                                stages.placing))))
 | 
					                                stages.placing))
 | 
				
			||||||
 | 
					            (if (not endtime) (tset self :player (self:next-player)))
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
      1 ;; 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:phase-two?) stages.moving stages.placing))
 | 
				
			||||||
          (tset self.moves (index-of-move move) self.player)
 | 
					          (tset self.moves (index-of-move move) self.player)
 | 
				
			||||||
          (let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves self.player)))
 | 
					          (let [flytime (and (self:phase-two?) (= 3 (player-count self.moves self.player)))
 | 
				
			||||||
                movetime (and (> self.pieces-placed 17) (> (player-count self.moves self.player) 3))
 | 
					                movetime (and (self:phase-two?) (> (player-count self.moves self.player) 3))
 | 
				
			||||||
                capturetime (mill-at? self.moves (index-of-move move))]
 | 
					                capturetime (mill-at? self.moves (index-of-move move))]
 | 
				
			||||||
            (tset self :stage (if
 | 
					            (tset self :stage (if
 | 
				
			||||||
                                capturetime stages.capture
 | 
					                                capturetime stages.capture
 | 
				
			||||||
@ -88,13 +92,15 @@
 | 
				
			|||||||
              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)
 | 
				
			||||||
          (let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves (self:next-player))))
 | 
					          (let [flytime (and (self:phase-two?) (= 3 (player-count self.moves (self:next-player))))
 | 
				
			||||||
                movetime (and (> self.pieces-placed 17) (> (player-count self.moves (self:next-player)) 3))
 | 
					                movetime (and (self:phase-two?) (> (player-count self.moves (self:next-player)) 3))
 | 
				
			||||||
                capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))]
 | 
					                capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))
 | 
				
			||||||
 | 
					                endtime (no-moves? self.moves (self:next-player))]
 | 
				
			||||||
            (tset self :stage (if
 | 
					            (tset self :stage (if
 | 
				
			||||||
                                capturetime stages.capture
 | 
					                                capturetime stages.capture
 | 
				
			||||||
                                flytime stages.flying
 | 
					                                flytime stages.flying
 | 
				
			||||||
                                movetime stages.moving
 | 
					                                movetime stages.moving
 | 
				
			||||||
 | 
					                                endtime stages.complete
 | 
				
			||||||
                                stages.placing))
 | 
					                                stages.placing))
 | 
				
			||||||
            (if (not capturetime) (tset self :player (self:next-player)))))
 | 
					            (if (not capturetime) (tset self :player (self:next-player)))))
 | 
				
			||||||
        3 ;; FLYING
 | 
					        3 ;; FLYING
 | 
				
			||||||
@ -102,8 +108,8 @@
 | 
				
			|||||||
                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)
 | 
				
			||||||
            (let [flytime (and (> self.pieces-placed 17) (= 3 (player-count self.moves (self:next-player))))
 | 
					            (let [flytime (and (self:phase-two?) (= 3 (player-count self.moves (self:next-player))))
 | 
				
			||||||
                  movetime (and (> self.pieces-placed 17) (> (player-count self.moves (self:next-player)) 3))
 | 
					                  movetime (and (self:phase-two?) (> (player-count self.moves (self:next-player)) 3))
 | 
				
			||||||
                  capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))]
 | 
					                  capturetime (mill-at? self.moves (index-of-move (string.sub move -2 -1)))]
 | 
				
			||||||
              (tset self :stage (if
 | 
					              (tset self :stage (if
 | 
				
			||||||
                                  capturetime stages.capture
 | 
					                                  capturetime stages.capture
 | 
				
			||||||
@ -111,10 +117,17 @@
 | 
				
			|||||||
                                  movetime stages.moving
 | 
					                                  movetime stages.moving
 | 
				
			||||||
                                  stages.placing))
 | 
					                                  stages.placing))
 | 
				
			||||||
              (if (not capturetime) (tset self :player (self:next-player)))))
 | 
					              (if (not capturetime) (tset self :player (self:next-player)))))
 | 
				
			||||||
 | 
					        5 ;; COMPLETE
 | 
				
			||||||
 | 
					          (print "Unreachable!")
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    (tset self :turns (+ self.turns 1))
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  :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))
 | 
				
			||||||
  :pieces-placed 0
 | 
					  :pieces-placed 0
 | 
				
			||||||
 | 
					  :turns 0
 | 
				
			||||||
 | 
					  ; so basically there's phase 1 where you place your checkers
 | 
				
			||||||
 | 
					  ; and then phase 2 when you move and fly around trying to capture pieces
 | 
				
			||||||
 | 
					  :phase-two? (fn [self] (> self.pieces-placed 17))
 | 
				
			||||||
  :init (fn [self]
 | 
					  :init (fn [self]
 | 
				
			||||||
    ; initialize moves[] to 0.
 | 
					    ; initialize moves[] to 0.
 | 
				
			||||||
    ; this is the game state.
 | 
					    ; this is the game state.
 | 
				
			||||||
@ -130,11 +143,6 @@
 | 
				
			|||||||
(game:init)
 | 
					(game:init)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; TODO: move to lib utility
 | 
					 | 
				
			||||||
(fn string-upper [s]
 | 
					 | 
				
			||||||
  (.. (string.upper (string.sub s 1 1)) (string.sub s 2)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Print! That! Board!
 | 
					; Print! That! Board!
 | 
				
			||||||
(fn print-board [board moves]
 | 
					(fn print-board [board moves]
 | 
				
			||||||
  (var index 1)
 | 
					  (var index 1)
 | 
				
			||||||
@ -143,12 +151,13 @@
 | 
				
			|||||||
      (if (> slots 0)
 | 
					      (if (> slots 0)
 | 
				
			||||||
        (do
 | 
					        (do
 | 
				
			||||||
          (let [offset (+ index slots)
 | 
					          (let [offset (+ index slots)
 | 
				
			||||||
                myslice (slice moves index offset)]
 | 
					                myslice (tbl.slice moves index offset)]
 | 
				
			||||||
            (print (string.format row-template (table.unpack myslice)))
 | 
					            (print (string.format row-template (table.unpack myslice)))
 | 
				
			||||||
            (set index offset)))
 | 
					            (set index offset)))
 | 
				
			||||||
        (print row))))
 | 
					        (print row))))
 | 
				
			||||||
  (print (.. "Stage: " (string-upper (. (kvflip stages) game.stage))))
 | 
					  (print (.. "Stage: " (str.capitalize (. (tbl.invert stages) game.stage))))
 | 
				
			||||||
  (print (.. "Player " game.player "'s turn:")))
 | 
					  (print (.. "Player " game.player "'s turn:")))
 | 
				
			||||||
 | 
					(local with-board (partial print-board const.board))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; add the inverse of each valid move
 | 
					; add the inverse of each valid move
 | 
				
			||||||
@ -161,7 +170,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; does the move exist within the domain of valid spaces
 | 
					; does the move exist within the domain of valid spaces
 | 
				
			||||||
(fn space-exists? [m] (contains const.spaces (string.upper m)))
 | 
					(fn space-exists? [m] (tbl.contains const.spaces (string.upper m)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; is the space represented by a [A-Za-z0-9] move unoccupied?
 | 
					; is the space represented by a [A-Za-z0-9] move unoccupied?
 | 
				
			||||||
@ -201,8 +210,7 @@
 | 
				
			|||||||
      (or (or (all-mills? game.moves game.player)
 | 
					      (or (or (all-mills? game.moves game.player)
 | 
				
			||||||
              (not (mill-at? game.moves (index-of-move move))))
 | 
					              (not (mill-at? game.moves (index-of-move move))))
 | 
				
			||||||
          (print "Ma'am, it is ILLEGAL to break up a mill.")
 | 
					          (print "Ma'am, it is ILLEGAL to break up a mill.")
 | 
				
			||||||
          )
 | 
					          ))
 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    (and
 | 
					    (and
 | 
				
			||||||
      (= stages.moving game.stage)
 | 
					      (= stages.moving game.stage)
 | 
				
			||||||
      (or (moving-format? move)
 | 
					      (or (moving-format? move)
 | 
				
			||||||
@ -212,8 +220,7 @@
 | 
				
			|||||||
      (or (space-is-unoccupied? (string.sub move -2 -1))
 | 
					      (or (space-is-unoccupied? (string.sub move -2 -1))
 | 
				
			||||||
          (print "That space is occupied!")) 
 | 
					          (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)))
 | 
					      (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")) 
 | 
					          (print "That ain't your neighbor, Johnny")) )
 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    (and
 | 
					    (and
 | 
				
			||||||
      (= stages.flying game.stage)
 | 
					      (= stages.flying game.stage)
 | 
				
			||||||
      (or (moving-format? move)
 | 
					      (or (moving-format? move)
 | 
				
			||||||
@ -221,8 +228,7 @@
 | 
				
			|||||||
      (or (not (space-is-occupied-by-opponent? (string.sub move 1 2)))
 | 
					      (or (not (space-is-occupied-by-opponent? (string.sub move 1 2)))
 | 
				
			||||||
          (print "That's not yours, don't touch it."))
 | 
					          (print "That's not yours, don't touch it."))
 | 
				
			||||||
      (or (space-is-unoccupied? (string.sub move -2 -1))
 | 
					      (or (space-is-unoccupied? (string.sub move -2 -1))
 | 
				
			||||||
          (print "That space is occupied!")) 
 | 
					          (print "That space is occupied!")))
 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -235,8 +241,7 @@
 | 
				
			|||||||
(fn main []
 | 
					(fn main []
 | 
				
			||||||
  ;; game loop
 | 
					  ;; game loop
 | 
				
			||||||
  (while (not (= game.stage stages.complete))
 | 
					  (while (not (= game.stage stages.complete))
 | 
				
			||||||
    (print-board const.board game.moves)
 | 
					    (with-board game.moves)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    ;; validation loop
 | 
					    ;; validation loop
 | 
				
			||||||
    (var is-valid false)
 | 
					    (var is-valid false)
 | 
				
			||||||
    (var move "")
 | 
					    (var move "")
 | 
				
			||||||
@ -246,11 +251,10 @@
 | 
				
			|||||||
      (if (not is-valid)
 | 
					      (if (not is-valid)
 | 
				
			||||||
        (print "Try again.")
 | 
					        (print "Try again.")
 | 
				
			||||||
        (do
 | 
					        (do
 | 
				
			||||||
          (print (.. "You chose " move))
 | 
					          (print (string.format "Turn %d: You chose %s" game.turns move))
 | 
				
			||||||
          (game:update move)
 | 
					          (game:update move)))))
 | 
				
			||||||
          )
 | 
					  ;; game is complete
 | 
				
			||||||
        )
 | 
					  (print "Congratulations!")
 | 
				
			||||||
    )
 | 
					  (print (string.format "Player %d is the winner!" game.player))
 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
(main)
 | 
					(main)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								test/capture-mill-no-mill.dat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								test/capture-mill-no-mill.dat
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					# this creates a board with with to test the
 | 
				
			||||||
 | 
					# "Unless There's No Other Option" exception
 | 
				
			||||||
 | 
					# to the "No Breaking Up Mills" capture rule.
 | 
				
			||||||
 | 
					# Player 1 is in a position to capture F2 or
 | 
				
			||||||
 | 
					# F4, and should not be able to capture any
 | 
				
			||||||
 | 
					# checkers from the B2-B4-B6 mill
 | 
				
			||||||
 | 
					a1
 | 
				
			||||||
 | 
					b2
 | 
				
			||||||
 | 
					a4
 | 
				
			||||||
 | 
					b4
 | 
				
			||||||
 | 
					d7
 | 
				
			||||||
 | 
					b6
 | 
				
			||||||
 | 
					d7
 | 
				
			||||||
 | 
					f2
 | 
				
			||||||
 | 
					d6
 | 
				
			||||||
 | 
					f4
 | 
				
			||||||
 | 
					a7
 | 
				
			||||||
@ -1,3 +1,7 @@
 | 
				
			|||||||
 | 
					## This sets up the board to test the "Unless There's No Other Option" 
 | 
				
			||||||
 | 
					#  exception to the "No Breaking Up Mills" capture rule. Player 2 is
 | 
				
			||||||
 | 
					#  ready to capture, but all of Player 1's checkers are in a mill.
 | 
				
			||||||
 | 
					#  So Player 2 should be able to capture any piece from the G1-G4-G7 mill.
 | 
				
			||||||
# PLACING PHASE (18 moves)
 | 
					# PLACING PHASE (18 moves)
 | 
				
			||||||
A1
 | 
					A1
 | 
				
			||||||
A4
 | 
					A4
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user