Move error handling to front-end
game.validate-move will now return an error code is the move is invalid. default error messages are found in lib/constants.fnl. error handling is now handled on the front end.
This commit is contained in:
		
							parent
							
								
									c7b2c98200
								
							
						
					
					
						commit
						9bf31e86ce
					
				@ -272,5 +272,44 @@ and will be spending a lot of time on my back.
 | 
				
			|||||||
So I'm either going to get a lot on 9mm,
 | 
					So I'm either going to get a lot on 9mm,
 | 
				
			||||||
or nothing at all.
 | 
					or nothing at all.
 | 
				
			||||||
We'll see!
 | 
					We'll see!
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP "20 - 21"
 | 
				
			||||||
 | 
					I was having back surgery!
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.
 | 
				
			||||||
 | 
					.IP "WEEK THREE REVIEW"
 | 
				
			||||||
 | 
					Finished the game, and then had a major surgery.
 | 
				
			||||||
 | 
					Doing some tidying up and housekeeping now.
 | 
				
			||||||
 | 
					Organzing tests,
 | 
				
			||||||
 | 
					breaking stuff out into modules.
 | 
				
			||||||
 | 
					Rewriting and refactoring a couple of functions.
 | 
				
			||||||
 | 
					Generally getting ready to start thinking
 | 
				
			||||||
 | 
					about Stretch Goal 1: Story Mode.
 | 
				
			||||||
 | 
					I have a vague inkling of an idea about
 | 
				
			||||||
 | 
					doing some kind of generative story / narration
 | 
				
			||||||
 | 
					based on each move of the game.
 | 
				
			||||||
 | 
					Assign symbolic meaning to the outer, middle, and inner rings of the board.
 | 
				
			||||||
 | 
					Add a little twist based on whether the move is a capture, a slide, a placement.
 | 
				
			||||||
 | 
					That kind of thing.
 | 
				
			||||||
 | 
					Not sure how I want to do it exactly yet.
 | 
				
			||||||
 | 
					I'd like to be able to show the story progress as you play.
 | 
				
			||||||
 | 
					But updating the story every move seems like kind of a lot.
 | 
				
			||||||
 | 
					Maybe just at every game phase.
 | 
				
			||||||
 | 
					So three installments.
 | 
				
			||||||
 | 
					One at the end of Placement,
 | 
				
			||||||
 | 
					the second at the end of Movement,
 | 
				
			||||||
 | 
					and the third at the end of the game?
 | 
				
			||||||
 | 
					Anyway.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Up next: big refactor of the validation logic.
 | 
				
			||||||
 | 
					I don't want it to just print to console
 | 
				
			||||||
 | 
					if I'm going to eventually support multiple frontends.
 | 
				
			||||||
 | 
					I need to return an error code or something
 | 
				
			||||||
 | 
					for the client to interpret.
 | 
				
			||||||
 | 
					I'm still interested in the Result / Either monad option
 | 
				
			||||||
 | 
					but I think I'm doing to first try the conventional lua way
 | 
				
			||||||
 | 
					and throw an error, and then 'pcall' the function and handle the error politely.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pl \n[nl]u
 | 
					.pl \n[nl]u
 | 
				
			||||||
 | 
				
			|||||||
@ -97,9 +97,33 @@
 | 
				
			|||||||
  :complete 5 ;; no more cows! jk the cows are fine. the game's just over okay
 | 
					  :complete 5 ;; no more cows! jk the cows are fine. the game's just over okay
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{: board
 | 
					
 | 
				
			||||||
 | 
					;; errror codes
 | 
				
			||||||
 | 
					(local errors {
 | 
				
			||||||
 | 
					  :not-a-space "That space does not exist!\nHint: 1a 1A A1 a1 are all the same move."
 | 
				
			||||||
 | 
					  :occupied "That space is occupied!"
 | 
				
			||||||
 | 
					  :not-an-opponent "Choose an opponent's piece to remove."
 | 
				
			||||||
 | 
					  :is-mill "Ma'am, it is ILLEGAL to break up a mill."
 | 
				
			||||||
 | 
					  :bad-move-format "Try a move like B2B4 or A7 D7"
 | 
				
			||||||
 | 
					  :not-yours "That's not yours, don't touch it."
 | 
				
			||||||
 | 
					  :not-neighbor "That ain't your neighbor, Johnny"
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; if you like it then you shoulda put a ...
 | 
				
			||||||
 | 
					(local rings {
 | 
				
			||||||
 | 
					  :outer  [ 1 2 3 15 24 23 22 10 ]
 | 
				
			||||||
 | 
					  :middle [ 4 5 6 14 21 20 19 11 ]
 | 
				
			||||||
 | 
					  :inner  [ 7 8 9 13 18 17 16 12 ]  
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					 : board
 | 
				
			||||||
 | 
					 : errors
 | 
				
			||||||
 : mills
 | 
					 : mills
 | 
				
			||||||
 : neighbors
 | 
					 : neighbors
 | 
				
			||||||
 | 
					 : rings
 | 
				
			||||||
 | 
					 : spaces
 | 
				
			||||||
 : stages
 | 
					 : stages
 | 
				
			||||||
 : spaces}
 | 
					 }
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								main.fnl
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								main.fnl
									
									
									
									
									
								
							@ -34,17 +34,23 @@
 | 
				
			|||||||
  ;; game loop
 | 
					  ;; game loop
 | 
				
			||||||
  (while (not (= game.stage const.stages.complete))
 | 
					  (while (not (= game.stage const.stages.complete))
 | 
				
			||||||
    (with-board game.moves)
 | 
					    (with-board game.moves)
 | 
				
			||||||
    ;; validation loop
 | 
					 | 
				
			||||||
    (var is-valid false)
 | 
					    (var is-valid false)
 | 
				
			||||||
    (var move "")
 | 
					    (var move "")
 | 
				
			||||||
 | 
					    ;; validation loop
 | 
				
			||||||
    (while (not is-valid)
 | 
					    (while (not is-valid)
 | 
				
			||||||
      (set move (get-move))
 | 
					      (set move (get-move))
 | 
				
			||||||
      (set is-valid (game.validate-move move))
 | 
					      (case (pcall game.validate-move move)
 | 
				
			||||||
      (if (not is-valid)
 | 
					        (false msg)
 | 
				
			||||||
        (print "Try again.")
 | 
					          (do
 | 
				
			||||||
        (do
 | 
					            (let [(i j) (string.find msg ": ")
 | 
				
			||||||
          (print (string.format "Turn %d: You chose %s" game.turns move))
 | 
					                  key (string.sub msg (+ 1 j))]
 | 
				
			||||||
          (game:update move)))))
 | 
					              (print (. const.errors key)))
 | 
				
			||||||
 | 
					            (print "Try again."))
 | 
				
			||||||
 | 
					        ok
 | 
				
			||||||
 | 
					          (do
 | 
				
			||||||
 | 
					            (set is-valid true)
 | 
				
			||||||
 | 
					            (print (string.format "Turn %d: You chose %s" game.turns move))
 | 
				
			||||||
 | 
					            (game:update move)))))
 | 
				
			||||||
  ;; game is complete
 | 
					  ;; game is complete
 | 
				
			||||||
  (print "Congratulations!")
 | 
					  (print "Congratulations!")
 | 
				
			||||||
  (print (string.format "Player %d is the winner!" game.player)))
 | 
					  (print (string.format "Player %d is the winner!" game.player)))
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								src/game.fnl
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								src/game.fnl
									
									
									
									
									
								
							@ -4,15 +4,15 @@
 | 
				
			|||||||
  } (require :lib.index))
 | 
					  } (require :lib.index))
 | 
				
			||||||
(local {
 | 
					(local {
 | 
				
			||||||
  : all-mills?
 | 
					  : all-mills?
 | 
				
			||||||
  :mill-at? mill-at-maker
 | 
					  :mill-at? full-mill-at ;; stay with me...
 | 
				
			||||||
  :no-moves? no-moves-maker
 | 
					  :no-moves? full-no-moves
 | 
				
			||||||
  :space-is-neighbor? space-is-neighbor-maker
 | 
					  :space-is-neighbor? full-space-is-neighbor
 | 
				
			||||||
  } (require :lib.game.index))
 | 
					  } (require :lib.game.index))
 | 
				
			||||||
(local const (require :lib.constants))
 | 
					(local const (require :lib.constants))
 | 
				
			||||||
;; front-loading with some partials
 | 
					;; front-loading the "fulls" with some partials
 | 
				
			||||||
(local mill-at? (partial mill-at-maker const.mills))
 | 
					(local mill-at? (partial full-mill-at const.mills))
 | 
				
			||||||
(local space-is-neighbor? (partial space-is-neighbor-maker const.neighbors))
 | 
					(local space-is-neighbor? (partial full-space-is-neighbor const.neighbors))
 | 
				
			||||||
(local no-moves? (partial no-moves-maker const.neighbors))
 | 
					(local no-moves? (partial full-no-moves const.neighbors))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;; story mode:
 | 
					;; story mode:
 | 
				
			||||||
@ -124,8 +124,7 @@
 | 
				
			|||||||
    ; 2 = Player 2
 | 
					    ; 2 = Player 2
 | 
				
			||||||
    ; NOTE: I think it might be a good idea to make moves
 | 
					    ; NOTE: I think it might be a good idea to make moves
 | 
				
			||||||
    ;       a list of moves. so that there can be undo and history
 | 
					    ;       a list of moves. so that there can be undo and history
 | 
				
			||||||
    (set self.moves (fcollect [i 1 24] 0))
 | 
					    (set self.moves (fcollect [i 1 24] 0)))
 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -164,40 +163,28 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; is this a legal move?
 | 
					; is this a legal move?
 | 
				
			||||||
 | 
					; note: string argument to assert funs are defined in /lib/constants
 | 
				
			||||||
(fn valid-move? [move]
 | 
					(fn valid-move? [move]
 | 
				
			||||||
  (or
 | 
					  (or
 | 
				
			||||||
    (and
 | 
					    (and
 | 
				
			||||||
      (= const.stages.placing game.stage)
 | 
					      (= const.stages.placing game.stage)
 | 
				
			||||||
      (or (space-exists? move)
 | 
					      (assert (space-exists? move) :not-a-space)
 | 
				
			||||||
          (print "That space does not exist!\nHint: 1a 1A A1 a1 are all the same move."))
 | 
					      (assert (space-is-unoccupied? move) :occupied))
 | 
				
			||||||
      (or (space-is-unoccupied? move)
 | 
					 | 
				
			||||||
          (print "That space is occupied!")))
 | 
					 | 
				
			||||||
    (and
 | 
					    (and
 | 
				
			||||||
      (= const.stages.capture game.stage)
 | 
					      (= const.stages.capture game.stage)
 | 
				
			||||||
      (or (space-is-occupied-by-opponent? move)
 | 
					      (assert (space-is-occupied-by-opponent? move) :not-an-opponent)
 | 
				
			||||||
          (print "Choose an opponent's piece to remove."))
 | 
					      (assert (or (all-mills? game.moves game.player) (not (mill-at? game.moves (index-of-move move)))) :is-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
 | 
					    (and
 | 
				
			||||||
      (= const.stages.moving game.stage)
 | 
					      (= const.stages.moving game.stage)
 | 
				
			||||||
      (or (moving-format? move)
 | 
					      (assert (moving-format? move) :bad-move-format) 
 | 
				
			||||||
          (print "Try a move like A1A2 or A7 D7")) 
 | 
					      (assert (not (space-is-occupied-by-opponent? (string.sub move 1 2))) :not-yours)
 | 
				
			||||||
      (or (not (space-is-occupied-by-opponent? (string.sub move 1 2)))
 | 
					      (assert (space-is-unoccupied? (string.sub move -2 -1)) :occupied) 
 | 
				
			||||||
          (print "That's not yours, don't touch it."))
 | 
					      (assert (space-is-neighbor? (index-of-move (string.sub move 1 2)) (index-of-move (string.sub move -2 -1))) :not-neighbor) )
 | 
				
			||||||
      (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
 | 
					    (and
 | 
				
			||||||
      (= const.stages.flying game.stage)
 | 
					      (= const.stages.flying game.stage)
 | 
				
			||||||
      (or (moving-format? move)
 | 
					      (assert (moving-format? move) :bad-move-format) 
 | 
				
			||||||
          (print "Try a move like A1A2 or A7 D7")) 
 | 
					      (assert (not (space-is-occupied-by-opponent? (string.sub move 1 2))) :not-yours)
 | 
				
			||||||
      (or (not (space-is-occupied-by-opponent? (string.sub move 1 2)))
 | 
					      (assert (space-is-unoccupied? (string.sub move -2 -1)) :occupied))))
 | 
				
			||||||
          (print "That's not yours, don't touch it."))
 | 
					 | 
				
			||||||
      (or (space-is-unoccupied? (string.sub move -2 -1))
 | 
					 | 
				
			||||||
          (print "That space is occupied!")))))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
(tset game :validate-move valid-move?)
 | 
					(tset game :validate-move valid-move?)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user