From 568ea6e2cce348396922f705dd2a8fa0cc14c166 Mon Sep 17 00:00:00 2001 From: nihilazo Date: Sun, 3 Apr 2022 18:50:25 +0100 Subject: [PATCH] complete game playing code, time to write some UI and bots! --- main-test.rkt | 44 ++++++++++++++++++++++---- main.rkt | 86 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 96 insertions(+), 34 deletions(-) diff --git a/main-test.rkt b/main-test.rkt index 3e1e2dc..67e5a4e 100644 --- a/main-test.rkt +++ b/main-test.rkt @@ -42,6 +42,44 @@ (check-true (out-of-bounds? (point -2 0))) (check-true (out-of-bounds? (point 0 -2)))) +(test-case + "boxes-for" + (check-equal? (boxes-for (line (point 0 0) (point 0 1) 1)) (list (point 0 0))) + (check-equal? (boxes-for (line (point 2 1) (point 2 2) 1)) (list (point 2 1) (point 1 1))) + (check-equal? (boxes-for (line (point 0 0) (point 1 0) 1)) (list (point 0 0))) + (check-equal? (boxes-for (line (point 1 1) (point 2 1) 1)) (list (point 1 1) (point 1 0))) + (check-equal? (boxes-for (line (point 0 5) (point 1 5) 1)) (list (point 0 4))) + (check-equal? (boxes-for (line (point 5 0) (point 5 1) 0)) (list (point 4 0)))) + +(test-case + "completes-boxes" + (define grid + (list + (line (point 0 0) (point 0 1) 1) + (line (point 0 1) (point 1 1) 0) + (line (point 1 0) (point 1 1) 1) + (line (point 0 2) (point 0 3) 1) + (line (point 0 3) (point 1 3) 1) + (line (point 0 2) (point 1 2) 1) + (line (point 0 4) (point 0 5) 0) + (line (point 0 4) (point 1 4) 1) + (line (point 1 4) (point 1 5) 0) + (line (point 3 0) (point 4 0) 0) + (line (point 3 0) (point 3 1) 1) + (line (point 4 0) (point 4 1) 0) + (line (point 3 5) (point 4 5) 1) + (line (point 3 4) (point 3 5) 0) + (line (point 3 4) (point 4 4) 1) + (line (point 4 5) (point 5 5) 1) + (line (point 4 4) (point 5 4) 0) + (line (point 5 4) (point 5 5) 1))) ; all possible unfinished boxes (plus an unfinished double) + (check-equal? (completes-boxes grid (line (point 0 0) (point 1 0) 0)) 1) + (check-equal? (completes-boxes grid (line (point 1 2) (point 1 3) 0)) 1) + (check-equal? (completes-boxes grid (line (point 3 1) (point 4 1) 0)) 1) + (check-equal? (completes-boxes grid (line (point 0 5) (point 1 5) 0)) 1) + (check-equal? (completes-boxes grid (line (point 4 4) (point 4 5) 0)) 2) + (check-equal? (completes-boxes grid (line (point 1 0) (point 2 0) 1)) 0)) + (test-case "valid-move?" (define grid (list @@ -88,12 +126,6 @@ (check-equal? (append-move grid (line (point 2 2) (point 2 3) 0)) (append grid (list (line (point 2 2) (point 2 3) 0)))) (check-equal? (append-move grid (line (point 0 0) (point 0 1) 0)) grid)) -(test-case - "boxes-for" - (check-equal? (boxes-for (line (point 0 0) (point 0 1) 1)) (list (point 0 0))) - (check-equal? (boxes-for (line (point 2 1) (point 2 2) 1)) (list (point 2 1) (point 1 1))) - (check-equal? (boxes-for (line (point 0 0) (point 1 0) 1)) (list (point 0 0))) - (check-equal? (boxes-for (line (point 1 1) (point 2 1) 1)) (list (point 1 1) (point 1 0)))) (test-case "valid-moves" diff --git a/main.rkt b/main.rkt index d96fe96..7faa188 100644 --- a/main.rkt +++ b/main.rkt @@ -2,7 +2,7 @@ (provide (all-defined-out)) ; for testing module -(require racket/draw racket/random racket/gui) +(require racket/draw racket/random) ; a Grid is a list of lines. All lines in a grid must be specified as moving left to right, top to bottom ("to" coordinate higher than "from" coordinate") @@ -12,6 +12,12 @@ ; represents a line drawn from one point to another and which player drew it. (struct line (from to player) #:transparent) +; represents the state of a game in play +; grid is the grid +; player is the current player +; scores is a list of numbers for the score of each player. +(struct GameState (grid player scores)) + ; width and height of the playing grid. (define GRID-WIDTH 6) (define GRID-HEIGHT 6) @@ -127,43 +133,67 @@ (define (boxes-for line) (filter (lambda (x) (not (eq? x 0))) (list - (line-from line) + (if (and + (not (= (- GRID-WIDTH 1) (point-x (line-from line)))) + (not (= (- GRID-HEIGHT 1) (point-y (line-from line))))) (line-from line) 0) (if (and (< (point-x (line-from line)) (point-x (line-to line))) ; horizontal line - (> (point-y (line-from line)) 0) - (< (point-y (line-from line)) (- GRID-HEIGHT 1))) - - (point (point-x (line-from line)) (- (point-y (line-from line)) 1)) 0) + (> (point-y (line-from line)) 0)) + (point (point-x (line-from line)) (- (point-y (line-from line)) 1)) 0) (if (and (< (point-y (line-from line)) (point-y (line-to line))) ; vertical line - (> (point-x (line-from line)) 0) - (< (point-y (line-from line)) (- GRID-WIDTH 1))) + (> (point-x (line-from line)) 0)) (point (- (point-x (line-from line)) 1) (point-y (line-from line))) 0)))) -; Grid Line -> bool -; returns true if this line in grid has completed a box. -(define (completed-box? g l) +; Grid Line -> bool or Number +; returns the number of boxes this completes +(define (completes-boxes grid l) (let - ([boxes (boxes-for l)]) - (or - (= (count-square (first boxes) g) 4) - (= (count-square (first boxes) g) 4)))) + ([boxes (boxes-for l)] + [g (append-move grid l)]) + (length (filter (lambda (b) (= (count-square b g) 4)) boxes)))) +; GameState, List of 2 Players -> GameState +; runs the game. +; If the game is over, returns the final game state +; else, lets a player play a move. If they complete a box, they stay on and earn a point. Else, the other player has a turn. +; TODO score 2 points for creating 2 boxes at once +(define (play-game s players) + (let ([g (GameState-grid s)] + [p (GameState-player s)]) + (cond + [(= (length g) (length ALL-LINES)) s] ; end of the game + [else + (let* ([move ((list-ref players p) g p)] + [new-grid (append-move g move)] + [boxes (completes-boxes g move)] + [box (not (= 0 boxes))] + [scores (GameState-scores s)]) + (play-game (GameState + new-grid + (if box p (abs (- p 1))) + (if box (list-set scores p (+ boxes (list-ref scores p))) scores)) players))]))) ; Grid -> Image ; renders grid to an image for showing humans the game. -(define (render-grid grid) - (let* ([bitmap (make-bitmap (* GRID-WIDTH GRID-SCALE) (* GRID-HEIGHT GRID-SCALE))] +(define (render-grid-bitmap grid) + (let* ([bitmap (make-bitmap (+ 10 (* GRID-WIDTH GRID-SCALE)) (+ 10 (* GRID-HEIGHT GRID-SCALE)))] ; add border for printing [dc (new bitmap-dc% [bitmap bitmap])]) - (for ([l grid]) - (cond - [(= (line-player l) 0) (send dc set-pen "red" 4 'solid)] ; player 1 draws in red - [(= (line-player l) 1) (send dc set-pen "blue" 4 'solid)]) ; player 2 draws in blue - (send dc draw-line - (+ 4 (* GRID-SCALE (point-x (line-from l)))) - (+ 4 (* GRID-SCALE (point-y (line-from l)))) - (+ 4 (* GRID-SCALE (point-x (line-to l)))) - (+ 4 (* GRID-SCALE (point-y (line-to l)))))) ; draw the line + (render-grid grid dc) + bitmap)) + +; Grid dc ! +; renders grid on the given drawing context. +; TODO color in squares +(define (render-grid grid dc) + (for ([l grid]) + (cond + [(= (line-player l) 0) (send dc set-pen "red" 4 'solid)] ; player 1 draws in red + [(= (line-player l) 1) (send dc set-pen "blue" 4 'solid)]) ; player 2 draws in blue + (send dc draw-line + (+ 4 (* GRID-SCALE (point-x (line-from l)))) + (+ 4 (* GRID-SCALE (point-y (line-from l)))) + (+ 4 (* GRID-SCALE (point-x (line-to l)))) + (+ 4 (* GRID-SCALE (point-y (line-to l)))))) ; draw the line - (send dc draw-bitmap EMPTY-GRID 0 0) ; overlay dot grid - bitmap)) \ No newline at end of file + (send dc draw-bitmap EMPTY-GRID 0 0)) ; overlay dot grid \ No newline at end of file