improved same game

- new drawing algorithm (makes the connected regions easier to see)
  - different scoring mechanism (give points for having few pieces left)
  - make the window resizable
  - general cleanup of the code
This commit is contained in:
Robby Findler 2011-01-09 18:13:32 -06:00
parent f1e13a7921
commit 2553553f09
2 changed files with 434 additions and 397 deletions

View File

@ -1,388 +1,426 @@
(module same mzscheme #lang racket/base
(require mzlib/etc (require racket/class
mzlib/class racket/unit
mzlib/unit racket/list
mred racket/gui/base
mzlib/list "../show-scribbling.ss")
"../show-scribbling.ss")
(provide game@) (provide game@)
(define game@ (define game@
(unit (unit
(import) (import)
(export) (export)
(define board-width 20) (define board-width 20)
(define board-height 10) (define board-height 10)
(define cell-size 30) (define colors (map (lambda (x) (make-object color% x))
(define colors (map (lambda (x) (make-object color% x)) (list "blue" "red" "magenta" "yellow" "cyan"))) (list "blue" "red" "brown" "forestgreen" "darkviolet")))
(define pens (map (lambda (x) (make-object pen% x 1 'solid)) colors)) (define pale-colors (map (λ (x)
(define brushes (map (lambda (x) (make-object brush% x 'solid)) colors)) (define (paleize x) (- 255 (floor (/ (- 255 x) 2))))
(define white-pen (make-object pen% "white" 1 'solid)) (make-object color%
(define white-brush (make-object brush% "white" 'solid)) (paleize (send x red))
(paleize (send x green))
(paleize (send x blue))))
colors))
;; build-board : (-> (vectorof (vectorof (vector (union num #f) boolean)))) ;; these are the sizes that the on-paint callback draws at;
; this represents the board. Each entry is the color index of ;; a scaling factor is applied to make the board fit the window
; the piece and a node to mark for the depth-first traversal. (define cell-w 11)
; #f for the color index indicates an eliminated piece. (define cell-h 11)
(define (build-board) (define pen-size 10)
(build-vector
board-width
(lambda (i)
(build-vector
board-height
(lambda (j)
(vector
(begin
(if (= j (- board-height 1))
(- (length colors) 1)
(modulo i (- (length colors) 1)))
(random (length colors)))
#f))))))
(define board (build-board)) (define (blob-sel-x b) (vector-ref b 1))
(define (blob-sel-y b) (vector-ref b 2))
(define game-over? #f) ;; build-board : (-> (vectorof (vectorof (vector (union num #f) boolean))))
; this represents the board. Each entry is the color index of
; the piece and a node to mark for the depth-first traversal.
; #f for the color index indicates an eliminated piece.
(define (build-board)
(build-vector
board-width
(lambda (i)
(build-vector
board-height
(lambda (j)
(vector
(begin
(if (zero? (modulo (+ j i) 4))
0
1)
#;
(if (zero? (modulo (+ j i) 2))
0
1)
#;
(if (= j (- board-height 1))
(- (length colors) 1)
(modulo i (- (length colors) 1)))
;0
#;(random (length colors)))
#f))))))
(define score 0) (define board (build-board))
(define (calc-score n)
(cond
[(= n 2) 2]
[else (- (* (- n 1) (- n 1)) (- n 3))]))
(define same-canvas% (define game-over? #f)
(class canvas%
(inherit get-dc get-client-size)
(define/private (get-width) (let-values ([(w h) (get-client-size)]) w))
(define/private (get-height) (let-values ([(w h) (get-client-size)]) h))
(define/private (get-x-step) (/ (get-width) board-width))
(define/private (get-y-step) (/ (get-height) board-height))
[define/public draw-cell
(lambda (dc highlight? i j)
(let ([index (vector-ref (vector-ref (vector-ref board i) j) 0)]
[x (* i (get-x-step))]
[y (* j (get-y-step))])
(send dc set-brush white-brush)
(send dc set-pen white-pen)
(send dc draw-rectangle x y (get-x-step) (get-y-step))
(when index
(send dc set-brush (list-ref brushes index))
(send dc set-pen (list-ref pens index))
(cond
[highlight?
(send dc draw-ellipse
(floor (+ x (/ (get-x-step) 4)))
(floor (+ y (/ (get-y-step) 4)))
(floor (/ (get-x-step) 2))
(floor (/ (get-y-step) 2)))]
[else
(send dc draw-ellipse x y (get-x-step) (get-y-step))]))))]
[define/public draw-line ;; adds up as the user clicks
(lambda (dc i) (define clicked-score 0)
(let ([show-turned? (> (length turned) 1)]) (define (calc-score n) (* n n))
(let loop ([j board-height]) (define (reset-score)
(cond (set! clicked-score 0)
[(zero? j) (void)] (set-score-label))
[else (define (update-score balls-going-away)
(draw-cell dc (and show-turned? (member (list i (- j 1)) turned)) i (- j 1)) (set! clicked-score (+ clicked-score (* balls-going-away balls-going-away)))
(loop (- j 1))]))))] (set-score-label))
(define (set-score-label)
[define/public find-same-colors (define penalty 0)
(lambda (i j) (for ([v (in-vector board)])
(let* ([index (vector-ref (vector-ref (vector-ref board i) j) 0)] (for ([v (in-vector v)])
[ans (when (vector-ref v 0)
(let loop ([i i] (set! penalty (+ penalty 10)))))
[j j] (send score-message set-label
[ps null]) (format "~a - ~a = ~a"
(cond clicked-score
[(not (and (<= 0 i) (< i board-width) penalty
(<= 0 j) (< j board-height))) (- clicked-score penalty))))
ps]
[(vector-ref (vector-ref (vector-ref board i) j) 1) ps]
[(not (vector-ref (vector-ref (vector-ref board i) j) 0)) ps]
[(= index (vector-ref (vector-ref (vector-ref board i) j) 0))
(let ([v (vector-ref (vector-ref board i) j)])
(vector-set! v 1 #t)
(loop (+ i 1)
j
(loop (- i 1)
j
(loop i
(- j 1)
(loop i
(+ j 1)
(cons (list v i j) ps))))))]
[else ps]))])
(for-each (lambda (p) (vector-set! (first p) 1 #f)) ans)
ans))]
[define/public get-game-over-size
(lambda (dc)
(let ([border 5])
(let-values ([(text-width text-height d l) (send dc get-text-extent game-over)])
(let ([x (- (/ (get-width) 2) (/ text-width 2))]
[y (- (/ (get-height) 2) (/ text-height 2))])
(values x y text-width text-height border)))))]
[define/public paint-game-over
(lambda (dc)
(send dc set-font font)
(let-values ([(x y text-width text-height border) (get-game-over-size dc)])
(send dc set-pen white-pen)
(send dc set-brush white-brush)
(send dc draw-rectangle
(- x border) (- y border)
(+ text-width border border)
(+ text-height border border))
(send dc draw-text game-over x y)))]
(field (define same-canvas%
[game-over "Game Over"] (class canvas%
[font (make-object font% 24 'decorative 'normal 'normal #f)] (inherit get-dc get-client-size)
[turned null]) (define/private (get-width) (let-values ([(w h) (get-client-size)]) w))
(define/private (get-height) (let-values ([(w h) (get-client-size)]) h))
(define/private (get-x-step) (/ (get-width) board-width))
(define/private (get-y-step) (/ (get-height) board-height))
[define/public (call-with-dc proc) (define mouse-current-x #f)
;; Since we're not in `on-paint', need to manually (define mouse-current-y #f)
;; suspend and resume flushing, so that intermediate (define mouse-clicked-x #f)
;; states are not flushed to the screen. (define mouse-clicked-y #f)
(let ([dc (get-dc)]) (define mouse-over-chosen? #t)
(send dc suspend-flush)
(proc dc)
(send dc resume-flush))]
[define/private recalc/draw-turned (define/override (on-paint)
(lambda (i j) (define dc (get-dc))
(set! turned (map (lambda (xx) (list (second xx) (third xx))) (find-same-colors i j))) (send dc erase)
(cond (define-values (cw ch) (get-client-size))
[(> (length turned) 1) (define pen-size (- (floor (min cell-w cell-h)) 2))
(send this-message set-label (number->string (calc-score (length turned)))) (send dc set-brush "black" 'transparent)
(call-with-dc (send dc set-smoothing 'smoothed)
(lambda (dc) (send dc set-scale
(for-each (lambda (p) (draw-cell dc #t (first p) (second p))) turned)))] (/ cw (* board-width cell-w))
[else (/ ch (* board-height cell-h)))
(send this-message set-label "")]))] (define painted (make-hash))
(for* ([i (in-range 0 board-width)]
[j (in-range 0 board-height)])
(unless (hash-ref painted (xy->key i j) #f)
(define color (vector-ref (vector-ref (vector-ref board i) j) 0))
(when color
(define blob (find-same-colors i j))
(for ([x (in-list blob)])
(hash-set! painted (xy->key (blob-sel-x x) (blob-sel-y x)) #t))
(update-pen/draw-blob blob dc color cell-w cell-h i j))))
(when game-over?
(paint-game-over)))
[define/override on-event (define/private (update-pen/draw-blob blob dc color cell-w cell-h i j)
(lambda (evt) (define mouse-over? #f)
(let+ ([val x (send evt get-x)] (define mouse-clicked-over? #f)
[val y (send evt get-y)] (define multiple-cells? #f)
[val i (inexact->exact (floor (* (/ x (get-width)) board-width)))]
[val j (inexact->exact (floor (* (/ y (get-height)) board-height)))]) (when (or (number? mouse-current-x)
(number? mouse-clicked-x))
(for ([obj (in-list blob)])
(define x (blob-sel-x obj))
(define y (blob-sel-y obj))
(when (or (not (equal? x i))
(not (equal? y j)))
(set! multiple-cells? #t))
(when (and (equal? x mouse-current-x)
(equal? y mouse-current-y))
(set! mouse-over? #t))
(when (and (equal? x mouse-clicked-x)
(equal? y mouse-clicked-y))
(set! mouse-clicked-over? #t))))
(cond
[mouse-clicked-x ;; has the mouse been clicked in a clickable place?
(cond
[(and mouse-over? mouse-clicked-over? multiple-cells?)
(send dc set-pen
(list-ref colors color)
(* pen-size 2/3)
'solid)
(draw-blob blob i j)
(send dc set-pen
(list-ref pale-colors color)
(* pen-size 2/3 1/2)
'solid)
(draw-blob blob i j)]
[(and mouse-over? mouse-clicked-over?)
(send dc set-pen
(list-ref colors color)
pen-size
'solid)
(draw-blob blob i j)]
[else
(send dc set-pen
(list-ref colors color)
pen-size
'solid)
(draw-blob blob i j)])]
[else
(send dc set-pen (list-ref colors color) pen-size 'solid)
(draw-blob blob i j)
(when mouse-over?
(send dc set-pen
(list-ref pale-colors color)
(* pen-size 2/3)
'solid)
(draw-blob blob i j))]))
(define (draw-blob blob i j)
(define dc (get-dc))
(define (connect x1 y1 x2 y2)
(send dc draw-line
(+ (/ cell-w 2) (* x1 cell-w))
(+ (/ cell-h 2) (* y1 cell-h))
(+ (/ cell-w 2) (* x2 cell-w))
(+ (/ cell-h 2) (* y2 cell-h))))
(cond
[(null? (cdr blob))
(define pt (car blob))
(connect (blob-sel-x pt) (blob-sel-y pt) (blob-sel-x pt) (blob-sel-y pt))]
[else
(for* ([b1 (in-list blob)]
[b2 (in-list blob)])
(when (= (+ (abs (- (blob-sel-x b1) (blob-sel-x b2)))
(abs (- (blob-sel-y b1) (blob-sel-y b2))))
1)
(connect (blob-sel-x b1) (blob-sel-y b1) (blob-sel-x b2) (blob-sel-y b2))))]))
(define/private (xy->key x y) (+ (* board-width y) x))
(define/public (find-same-colors i j)
(let* ([index (vector-ref (vector-ref (vector-ref board i) j) 0)]
[ans
(let loop ([i i]
[j j]
[ps null])
(cond (cond
[(or (send evt moving?) [(not (and (<= 0 i) (< i board-width)
(send evt entering?) (<= 0 j) (< j board-height)))
(send evt leaving?)) ps]
[(vector-ref (vector-ref (vector-ref board i) j) 1) ps]
[(not (vector-ref (vector-ref (vector-ref board i) j) 0)) ps]
[(= index (vector-ref (vector-ref (vector-ref board i) j) 0))
(let ([v (vector-ref (vector-ref board i) j)])
(vector-set! v 1 #t)
(loop (+ i 1)
j
(loop (- i 1)
j
(loop i
(- j 1)
(loop i
(+ j 1)
(cons (vector v i j)
ps))))))]
[else ps]))])
(for-each (lambda (p) (vector-set! (vector-ref p 0) 1 #f)) ans)
ans))
(define/public (paint-game-over)
(define dc (get-dc))
(send dc set-font font)
(define border 5)
(define-values (text-width text-height d l)
(send dc get-text-extent game-over))
(define x (- (/ (* cell-w board-width) 2) (/ text-width 2)))
(define y (- (/ (* cell-h board-height) 2) (/ text-height 2)))
(send dc set-pen "white" 1' transparent)
(send dc set-brush "white" 'solid)
(send dc set-alpha .8)
(send dc draw-rectangle
(- x border border) (- y border)
(+ text-width border border border border)
(+ text-height border border))
(send dc set-alpha 1)
(send dc draw-text game-over x y))
[define game-over "Game Over"]
[define font (make-object font% 24 'decorative 'normal 'normal #f)]
[define turned null]
[define/public (call-with-dc proc)
;; Since we're not in `on-paint', need to manually
;; suspend and resume flushing, so that intermediate
;; states are not flushed to the screen.
(let ([dc (get-dc)])
(send dc suspend-flush)
(proc dc)
(send dc resume-flush))]
(inherit refresh)
(define/override (on-event evt)
(define x (send evt get-x))
(define y (send evt get-y))
(define-values (cw ch) (get-client-size))
(define bx (floor (* (/ x cw) board-width)))
(define by (floor (* (/ y ch) board-height)))
(unless (<= 0 bx (- board-width 1)) (set! bx #f))
(unless (<= 0 by (- board-height 1)) (set! by #f))
(when (send evt leaving?)
(set! bx #f)
(set! by #f))
(when (send evt button-up?)
(when (and (equal? mouse-clicked-x bx)
(equal? mouse-clicked-y by))
(make-a-move)
(update-game-over)
(refresh)))
(define-values (new-mouse-clicked-x new-mouse-clicked-y)
(cond
[(send evt button-down?) (values bx by)]
[(send evt button-up?) (values #f #f)]
[else (values mouse-clicked-x mouse-clicked-y)]))
(unless (and (equal? mouse-clicked-x new-mouse-clicked-x)
(equal? mouse-clicked-y new-mouse-clicked-y))
(set! mouse-clicked-x new-mouse-clicked-x)
(set! mouse-clicked-y new-mouse-clicked-y)
(refresh))
(unless (and (equal? bx mouse-current-x)
(equal? by mouse-current-y))
(set! mouse-current-x bx)
(set! mouse-current-y by)
(refresh)))
(define/private (make-a-move)
(define i mouse-clicked-x)
(define j mouse-clicked-y)
(let ([same-colors (find-same-colors i j)])
(when (>= (length same-colors) 2)
;; slide down empty pieces
(let ([is null])
(for-each
(lambda (p)
(let ([i (blob-sel-x p)]
[j (blob-sel-y p)])
(unless (member i is)
(set! is (cons i is)))
(let loop ([x j])
(cond (cond
[(and (<= 0 i) (< i board-width) [(<= 1 x)
(<= 0 j) (< j board-height)) (let ([next (vector-ref (vector-ref board i) (- x 1))]
(unless (member (list i j) turned) [this (vector-ref (vector-ref board i) x)])
(when (> (length turned) 1) (vector-set! this 0 (vector-ref next 0))
(call-with-dc (loop (- x 1)))]
(lambda (dc)
(for-each (lambda (p) (draw-cell dc #f (first p) (second p))) turned))))
(recalc/draw-turned i j))]
[else [else
(when (> (length turned) 1) (vector-set! (vector-ref (vector-ref board i) x) 0 #f)]))))
(call-with-dc (sort same-colors
(lambda (dc) (lambda (x y) (<= (blob-sel-y x) (blob-sel-y y)))))
(for-each (lambda (p) (draw-cell dc #f (first p) (second p))) turned))))
(set! turned null)
(send this-message set-label "")])]
[(send evt button-up?)
(when (and (<= 0 i) (< i board-width)
(<= 0 j) (< j board-height))
(when (> (length turned) 1)
(call-with-dc
(lambda (dc)
(for-each (lambda (p) (draw-cell dc #f (first p) (second p))) turned))))
(set! turned null)
(send this-message set-label "")
(let ([same-colors (find-same-colors i j)])
;; reset back the marks for the next depth-first traversal ;; slide empty over empty rows
(set! is (sort is >))
(let ([empty-is
(filter (lambda (i)
(not (vector-ref
(vector-ref (vector-ref board i) (- board-height 1))
0)))
is)])
(let ([is (if (null? empty-is)
is
(filter (lambda (x) (< x (car empty-is)))
is))])
(for-each (lambda (empty-i)
(let loop ([i empty-i])
(cond
[(<= i (- board-width 2))
(vector-set! board i (vector-ref board (+ i 1)))
(loop (+ i 1))]
[(= i (- board-width 1))
(vector-set!
board
i
(build-vector board-height
(λ (i) (vector #f #f))))])))
empty-is))))
(when (>= (length same-colors) 2)
;; update score ;; tally disappearing balls
(set! score (+ score (calc-score (length same-colors)))) (update-score (length same-colors)))))
(send message set-label (number->string score))
;; slide down empty pieces (define/public-final (update-game-over)
(let ([is null]) (set! game-over?
(for-each (not
(lambda (p) (let loop ([i board-width]
(let ([i (second p)] [continue? #f])
[j (third p)]) (cond
(unless (member i is) [(zero? i) continue?]
(set! is (cons i is))) [else
(let loop ([x j]) (or continue?
(cond (loop
[(<= 1 x) (sub1 i)
(let ([next (vector-ref (vector-ref board i) (- x 1))] (let loop ([j board-height]
[this (vector-ref (vector-ref board i) x)]) [continue? continue?])
(vector-set! this 0 (vector-ref next 0)) (cond
(loop (- x 1)))] [(zero? j) continue?]
[else [else
(vector-set! (vector-ref (vector-ref board i) x) 0 #f)])))) (or continue?
(sort same-colors (loop
(lambda (x y) (<= (third x) (third y))))) (sub1 j)
(> (length (find-same-colors (sub1 i) (sub1 j))) 1)))]))))])))))
;; slide empty over empty rows
(set! is (sort is >))
(let ([empty-is (filter (lambda (i)
(not (vector-ref (vector-ref (vector-ref board i) (- board-height 1)) 0)))
is)])
(let ([is (if (null? empty-is)
is
(filter (lambda (x) (< x (car empty-is)))
is))])
(for-each (lambda (empty-i)
(let loop ([i empty-i])
(cond
[(<= i (- board-width 2))
(vector-set! board i (vector-ref board (+ i 1)))
(loop (+ i 1))]
[(= i (- board-width 1))
(vector-set! board i (build-vector board-height
(lambda (i) (vector #f #f))))])))
empty-is)
;; draw changed lines
(call-with-dc
(lambda (dc)
(for-each (lambda (i) (draw-line dc i)) is)
(unless (null? empty-is)
(let loop ([i (car (last-pair empty-is))])
(cond
[(= i board-width) (void)]
[else (draw-line dc i)
(loop (+ i 1))])))))
;; update `small' balls (super-new)))
(recalc/draw-turned i j)
)))
(set! game-over? (define semaphore (make-semaphore 0))
(not (define same-frame%
(let loop ([i board-width] (class frame%
[continue? #f]) [define/augment on-close
(cond
[(zero? i) continue?]
[else
(or continue?
(loop
(sub1 i)
(let loop ([j board-height]
[continue? continue?])
(cond
[(zero? j) continue?]
[else
(or continue?
(loop
(sub1 j)
(> (length (find-same-colors (sub1 i) (sub1 j))) 1)))]))))]))))
(when game-over?
(call-with-dc (lambda (dc) (paint-game-over dc)))))))]
[else (void)])))]
[define/override on-paint
(lambda ()
(let ([dc (get-dc)])
(send dc set-pen white-pen)
(send dc set-brush white-brush)
(let loop ([i board-width])
(cond
[(zero? i) (void)]
[else (draw-line dc (- i 1))
(loop (- i 1))]))
(when game-over?
(paint-game-over dc))))]
(super-new)
(send (get-dc) set-smoothing 'aligned)))
(define semaphore (make-semaphore 0))
(define same-frame%
(class frame%
[define/augment on-close
(lambda ()
(semaphore-post semaphore)
(inner (void) on-close))]
(super-new [style '(metal)])))
(define find-largest-connected-region
(let ([biggest-so-far 0]
[tests 0])
(lambda () (lambda ()
(let ([answer 0]) (semaphore-post semaphore)
(let loop ([i 20]) (inner (void) on-close))]
(cond (super-new [style '(metal)])))
[(zero? i) (void)]
[else
(let loop ([j 10])
(cond
[(zero? j) (void)]
[else (set! answer
(max
answer
(length
(send canvas find-same-colors
(- i 1) (- j 1)))))
(loop (- j 1))]))
(loop (- i 1))]))
(set! biggest-so-far (max biggest-so-far (calc-score answer)))
(set! tests (+ tests 1))
(printf "tests: ~a sofar: ~a largest connected region: ~a score ~a\n"
tests
biggest-so-far
answer
(calc-score answer))))))
(define (new-game-callback redraw?) (define (new-game-callback redraw?)
(set! game-over? #f) (set! game-over? #f)
(set! board (build-board)) (set! board (build-board))
(unless (= score 0) (reset-score)
(set! score 0) (send canvas update-game-over)
(send message set-label "0")) (when redraw?
(send this-message set-label "") (send canvas refresh)))
(when redraw?
(send canvas on-paint)))
(define frame (make-object same-frame% "Same")) (define frame (make-object same-frame% "Same"))
(define panel (make-object vertical-panel% frame)) (define panel (make-object vertical-panel% frame))
(define canvas (make-object same-canvas% panel)) (define canvas (make-object same-canvas% panel))
(define hp (make-object horizontal-panel% panel)) (define hp (new horizontal-panel% [parent panel] [stretchable-height #f]))
(make-object message% "Total Score: " hp) (new message% [label "Total Score: "] [parent hp])
(define message (make-object message% "0" hp)) (define score-message (new message% [label ""] [parent hp] [stretchable-width #t]))
(make-object message% "This Score: " hp) (define button (make-object button% "New Game" hp (lambda x (new-game-callback #t))))
(define this-message (make-object message% "0" hp))
(define button (make-object button% "New Game" hp (lambda x (new-game-callback #t))))
'(make-object button% "Run Scores" hp (lambda x
(let loop ()
(new-game-callback #f)
(find-largest-connected-region)
(loop))))
(define help-button (make-object button% "Help" (define help-button (make-object button% "Help"
hp hp
(let ([show-help (let ([show-help
(show-scribbling (show-scribbling
'(lib "games/scribblings/games.scrbl") '(lib "games/scribblings/games.scrbl")
"same")]) "same")])
(lambda (_1 _2) (lambda (_1 _2)
(show-help))))) (show-help)))))
(send message stretchable-width #t) (send canvas update-game-over)
(send this-message stretchable-width #t) (reset-score)
(send hp stretchable-height #f) (send canvas min-width (* board-width cell-w 2))
(send canvas min-width (* board-width cell-size)) (send canvas min-height (* board-height cell-h 2))
(send canvas min-height (* board-height cell-size)) (send frame show #t)
(yield semaphore)))
(send frame show #t)
(yield semaphore))))
(invoke-unit game@)

View File

@ -3,19 +3,18 @@
@gametitle["Same" "same" "Dot-Removing Game"] @gametitle["Same" "same" "Dot-Removing Game"]
The object of @game{Same} is to score points by removing dots from the The object of @game{Same} is to score points by removing blobs from the
board. To remove a dot, click on it. As long as there is another dot board. To remove a blob, click on it. As long the blob is not just
of the same color next to the clicked dot, it will disappear along a simple circle, it will disappear. After the blob disappears,
with all adjacent dots of the same color. After the dots disappear, the remaining pieces of the board shift around, breaking up blobs into
dots in the rows above the deleted dots will fall into the vacated new blobs as pieces of the old blobs fall down to fill in the empty space.
spaces. If an entire column is wiped out, all of the dots from the If an entire column is wiped out, all of the blobs from the
right will slide left to take up the empty column's space. right will slide left to take up the empty column's space.
Your score increases for each ball removed from the board. The score Your score increases for each ball removed from the board. In general,
for each click is a function of the number of balls that disappeared. when you remove a blob, you get as many points as the square of the number
The @onscreen{This Click} label shows how many points you would score of cells the blob occupied, so removing bigger blobs is better. Also,
for clicking the dots underneath the mouse pointer. The score varies there is a penalty of 10 points for each colored cell left behind on the board,
quadratically with the number of balls, so eliminating many balls with so try to clear out the entire board.
one click is advantageous.
Click the @onscreen{New Game} button to play again. Click the @onscreen{New Game} button to play again.