; AisleRiot - sol.scm ; Copyright (C) 1998, 2003 Jonathan Blandford ; ; This game is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2, or (at your option) ; any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 ; USA (use-modules (ice-9 format)) ;; Compatibility code so we run on both guile 1.3 and 1.5 (define (eval-1 code) (if (stringvector DECK)) (len (vector-length vec))) (set! DECK (shuffle-deck-helper vec 0 len)))) ; The playing area is divided into slots, where cards can be placed. ; Each slot can hold any amount of cards. The slots are identified ; using numbers assigned in order of their creation. The deck of cards ; held in DECK should be assigned to one of the slots on creation. ; (You may then create another deck and place it in a later slot). ; ; The slots are added to the board from left to right until the ; add-carriage-return-slot procedure is called, which starts a new line. ; A space may be added using the add-blank-slot procedure. These false ; slots are not assigned identifiers. (define (add-blank-slot) (get-and-increment-position)) (define (add-carriage-return-slot) (linefeed-position)) ; The real slots come in three varieties: ; A slot in which only the topmost card is visible: (define (add-normal-slot cards) (add-slot (set-tag! (new-slot cards (list 'normal (get-and-increment-position)))))) ; A slot in which all the cards are visible, arranged as an overlapped pile: ; (either proceeding to the right or down). (define (add-extended-slot cards direction) (if (= right direction) (add-slot (set-tag! (new-slot cards (list 'expanded-right (get-and-increment-position))))) (add-slot (set-tag! (new-slot cards (list 'expanded (get-and-increment-position))))))) ; A slot in only the n topmost cards are visible: (define (add-partially-extended-slot cards direction n) (if (= right direction) (add-slot (set-tag! (new-slot cards (list 'partially-expanded-right (get-and-increment-position) n)))) (add-slot (set-tag! (new-slot cards (list 'partially-expanded (get-and-increment-position) n)))))) ; Cards may be dealt off one slot (usually the one containing the deck) ; and onto a list of other slots using these procedures: (define (deal-cards target-slot-id slot-list) (if (not (null? slot-list)) (begin (add-card! (car slot-list) (remove-card target-slot-id)) (deal-cards target-slot-id (cdr slot-list))))) (define (deal-cards-face-up target-slot-id slot-list) (if (not (null? slot-list)) (begin (add-card! (car slot-list) (make-visible (remove-card target-slot-id))) (deal-cards-face-up target-slot-id (cdr slot-list))))) ;; GENERAL GAME PROCEDURES ; these may be used in game files at any time. ;; Procedures that change slot contents: ; turn the top card of a slot over (face up to face down and vice versa) (define (flip-top-card slot-id) (add-card! slot-id (flip-card (remove-card slot-id)))) ; turn the top card of a slot face side up (define (make-visible-top-card slot-id) (add-card! slot-id (make-visible (remove-card slot-id)))) ; add a card onto the top of a slot (define (add-card! slot-id card) (set-cards! slot-id (cons card (get-cards slot-id)))) ; add a list of cards onto the top of a slot (define (add-cards! slot-id cards) (set-cards! slot-id (append cards (get-cards slot-id)))) ; remove (and return) the top card from a slot (define (remove-card slot-id) (let ((cards (get-cards slot-id))) (set-cards! slot-id (cdr cards)) (car cards))) ;; Utilities ; deal a card from the stock-slot to the waste-slot. ; when the stock slot is empty than the waste slot will be flipped back ; onto the stock unless the flip limit has been reached. ; an optional forth argument indicates the number of cards to deal. (define (flip-stock stock-slot waste-slot flip-limit . rest) (if (empty-slot? stock-slot) (and (< FLIP-COUNTER flip-limit) (set! FLIP-COUNTER (+ 1 FLIP-COUNTER)) (flip-deck stock-slot waste-slot)) (or (let loop ((i (if (null? rest) 1 (car rest)))) (and (> i 0) (not (empty-slot? stock-slot)) (add-card! waste-slot (flip-card (remove-card stock-slot))) (loop (- i 1)))) #t))) ; turn the cards in the waste slot over and add them to the stock-slot. (define (flip-deck stock-slot waste-slot) (and (not (empty-slot? waste-slot)) (add-card! stock-slot (flip-card (remove-card waste-slot))) (or (flip-deck stock-slot waste-slot) #t))) ;; Procedures for manipulating cards: ; NB: In order to use these procedures you must remove the cards ; from their slots and then replace them after applying the procedure ; (as in the make-top-card-visible procedure above) (define (flip-card card) (list (car card) (cadr card) (not (caddr card)))) (define (make-visible card) (list (car card) (cadr card) #t)) ;; Procedures that provide information only: ; card procedures (define (is-visible? card) (caddr card)) (define (get-suit card) (cadr card)) (define (get-color card) (cond ((eq? (get-suit card) club) black) ((eq? (get-suit card) spade) black) ((eq? (get-suit card) heart) red) ((eq? (get-suit card) diamond) red) (#t (_"Unknown color")))) (define (get-value card) (car card)) ; slot procedures (define (get-cards slot-id) (cadr (get-slot slot-id))) (define (empty-slot? slot-id) (null? (get-cards slot-id))) (define (get-top-card slot-id) (let ((cards (get-cards slot-id))) (if (null? cards) '() (car cards)))) ;; Utilities - need more of these: (define (cards-eq? card1 card2) (and (eq? (get-value card1) (get-value card2)) (eq? (get-suit card1) (get-suit card2)))) (define (is-red? card) (eq? red (get-color card))) (define (is-black? card) (eq? black (get-color card))) (define (is-joker? card) (= (get-value card) joker)) (define (set-ace-low) (set! ace 1)) (define (set-ace-high) (set! ace 14)) ; use to compare two cards when aces are treated as high: (define (ace-high-order value) (remainder (+ 11 value) 13)) (define (check-same-suit-list card-list) (or (< (length card-list) 2) (and (= (get-suit (car card-list)) (get-suit (cadr card-list))) (check-same-suit-list (cdr card-list))))) (define (check-same-color-list card-list) (or (< (length card-list) 2) (and (eq? (is-red? (car card-list)) (is-red? (cadr card-list))) (check-same-color-list (cdr card-list))))) (define (check-alternating-color-list card-list) (or (< (length card-list) 2) (and (eq? (is-black? (car card-list)) (is-red? (cadr card-list))) (check-alternating-color-list (cdr card-list))))) (define (check-straight-descending-list card-list) (or (< (length card-list) 2) (and (= (get-value (car card-list)) (- (get-value (cadr card-list)) 1)) (check-straight-descending-list (cdr card-list))))) ; debugging aid: (define (display-list . objs) (map display objs) (newline)) ; hint procedures (define (get-value-name value) (cond ((eq? value ace) (_"ace")) ((eq? value 2) (_"two")) ((eq? value 3) (_"three")) ((eq? value 4) (_"four")) ((eq? value 5) (_"five")) ((eq? value 6) (_"six")) ((eq? value 7) (_"seven")) ((eq? value 8) (_"eight")) ((eq? value 9) (_"nine")) ((eq? value 10) (_"ten")) ((eq? value jack) (_"jack")) ((eq? value queen) (_"queen")) ((eq? value king) (_"king")) (#t (_"Unknown value")))) (define (get-suit-name suit) (cond ((eq? suit club) (_"clubs")) ((eq? suit spade) (_"spades")) ((eq? suit heart) (_"hearts")) ((eq? suit diamond) (_"diamonds")) (#t (_"Unknown suit")))) (define (get-joker-name card) (if (is-black? card) (_"black joker") (_"red joker"))) (define (get-name card) (let ((value (get-value card)) (suit (get-suit card))) (if (is-joker? card) (get-joker-name card) (cond ((eq? suit club) (cond ((eq? value ace) (_"the ace of clubs")) ((eq? value 2) (_"the two of clubs")) ((eq? value 3) (_"the three of clubs")) ((eq? value 4) (_"the four of clubs")) ((eq? value 5) (_"the five of clubs")) ((eq? value 6) (_"the six of clubs")) ((eq? value 7) (_"the seven of clubs")) ((eq? value 8) (_"the eight of clubs")) ((eq? value 9) (_"the nine of clubs")) ((eq? value 10) (_"the ten of clubs")) ((eq? value jack) (_"the jack of clubs")) ((eq? value queen) (_"the queen of clubs")) ((eq? value king) (_"the king of clubs")) (#t (_"the unknown card")))) ((eq? suit spade) (cond ((eq? value ace) (_"the ace of spades")) ((eq? value 2) (_"the two of spades")) ((eq? value 3) (_"the three of spades")) ((eq? value 4) (_"the four of spades")) ((eq? value 5) (_"the five of spades")) ((eq? value 6) (_"the six of spades")) ((eq? value 7) (_"the seven of spades")) ((eq? value 8) (_"the eight of spades")) ((eq? value 9) (_"the nine of spades")) ((eq? value 10) (_"the ten of spades")) ((eq? value jack) (_"the jack of spades")) ((eq? value queen) (_"the queen of spades")) ((eq? value king) (_"the king of spades")) (#t (_"the unknown card")))) ((eq? suit heart) (cond ((eq? value ace) (_"the ace of hearts")) ((eq? value 2) (_"the two of hearts")) ((eq? value 3) (_"the three of hearts")) ((eq? value 4) (_"the four of hearts")) ((eq? value 5) (_"the five of hearts")) ((eq? value 6) (_"the six of hearts")) ((eq? value 7) (_"the seven of hearts")) ((eq? value 8) (_"the eight of hearts")) ((eq? value 9) (_"the nine of hearts")) ((eq? value 10) (_"the ten of hearts")) ((eq? value jack) (_"the jack of hearts")) ((eq? value queen) (_"the queen of hearts")) ((eq? value king) (_"the king of hearts")) (#t (_"the unknown card")))) ((eq? suit diamond) (cond ((eq? value ace) (_"the ace of diamonds")) ((eq? value 2) (_"the two of diamonds")) ((eq? value 3) (_"the three of diamonds")) ((eq? value 4) (_"the four of diamonds")) ((eq? value 5) (_"the five of diamonds")) ((eq? value 6) (_"the six of diamonds")) ((eq? value 7) (_"the seven of diamonds")) ((eq? value 8) (_"the eight of diamonds")) ((eq? value 9) (_"the nine of diamonds")) ((eq? value 10) (_"the ten of diamonds")) ((eq? value jack) (_"the jack of diamonds")) ((eq? value queen) (_"the queen of diamonds")) ((eq? value king) (_"the king of diamonds")) (#t (_"the unknown card")))) (#t (_"the unknown card")))))) (define (move-n-cards! start-slot end-slot cards) (add-cards! end-slot cards)) (define (remove-n-cards slot-id n) (set-cards! slot-id (nthcdr n (get-cards slot-id)))) (define (deal-cards-from-deck deck slot-list) (if (not (null? slot-list)) (begin (add-card! (car slot-list) (car deck)) (deal-cards-from-deck (cdr deck) (cdr slot-list))))) (define (deal-cards-face-up-from-deck deck slot-list) (if (not (null? slot-list)) (begin (add-card! (car slot-list) (make-visible (car deck))) (deal-cards-face-up-from-deck (cdr deck) (cdr slot-list))))) (define (set-cards! slot-id new_cards) ; (if IN-GAME ; (record-move slot-id new_cards (get-cards slot-id))) (set-cards-c! slot-id new_cards)) (define (make-card value suit) (list value suit #f)) (define (make-standard-deck-list-ace-high value suit) (if (eq? ace value) (if (eq? spade suit) (list (make-card ace spade)) (cons (make-card value suit) (make-standard-deck-list-ace-high 2 (+ 1 suit)))) (cons (make-card value suit) (make-standard-deck-list-ace-high (+ 1 value) suit)))) (define (make-standard-deck-list-ace-low value suit) (if (eq? king value) (if (eq? spade suit) (list (make-card king spade)) (cons (make-card value suit) (make-standard-deck-list-ace-low 1 (+ 1 suit)))) (cons (make-card value suit) (make-standard-deck-list-ace-low (+ 1 value) suit)))) (define (shuffle-deck-helper deck ref1 len) (if (zero? len) '() (let* ((ref2 (+ ref1 (random len))) (val-at-ref2 (vector-ref deck ref2))) (vector-set! deck ref2 (vector-ref deck ref1)) (cons val-at-ref2 (shuffle-deck-helper deck (+ ref1 1) (- len 1)))))) (define (new-slot deck placement) (list #f deck placement)) (define (set-tag! slot) (set! SLOTS (+ 1 SLOTS)) (cons (- SLOTS 1) (cdr slot))) (define (get-and-increment-position) (let ((retval (list HORIZPOS VERTPOS))) (set! HORIZPOS (+ HORIZPOS (get-horiz-offset))) retval)) (define (linefeed-position) (set! HORIZPOS (get-horiz-start)) (set! VERTPOS (+ VERTPOS (get-vert-offset)))) (define (register-undo-function function data) (set! MOVE (cons '(function data) (cdr MOVE)))) ; common lisp procedure not provided in guile 1.3 (define (nthcdr n lst) (if (zero? n) lst (nthcdr (+ -1 n) (cdr lst)))) ;; INTERNAL procedures ; global variables (define FLIP-COUNTER 0) (define SLOTS 0) (define HORIZPOS 0) (define VERTPOS 0) (define MOVE '()) (define HISTORY '()) (define FUTURE '()) (define IN-GAME #f) ; called from C: (define (start-game) (set! IN-GAME #t)) ; called from C: (define (end-move) (if (not (= 0 (length MOVE))) (begin (set! HISTORY (cons MOVE HISTORY)) (set! FUTURE '()) (set! MOVE '()) (if (null? HISTORY) (undo-set-sensitive #f) (undo-set-sensitive #t)) (if (null? FUTURE) (redo-set-sensitive #f) (redo-set-sensitive #t))))) (define (return-cards card-positions slot-id) (and (not (= 0 (length card-positions))) (set-cards! slot-id (car card-positions)) (return-cards (cdr card-positions) (+ 1 slot-id)))) (define (give-status-message) #t) (define (eval-move move) (return-cards (caddr move) 0) ((car move) (cadr move)) (give-status-message)) ; called from C: (define (undo) (and (not (null? HISTORY)) (record-move -1 '()) (eval-move (car HISTORY)) (set! FUTURE (cons MOVE FUTURE)) (set! HISTORY (cdr HISTORY)) (set! MOVE '()) (redo-set-sensitive #t) (if (null? HISTORY) (undo-set-sensitive #f)))) ; called from C: (define (redo) (and (not (null? FUTURE)) (record-move -1 '()) (eval-move (car FUTURE)) (set! HISTORY (cons MOVE HISTORY)) (set! FUTURE (cdr FUTURE)) (set! MOVE '()) (undo-set-sensitive #t) (if (null? FUTURE) (redo-set-sensitive #f)))) (define (undo-func data) (set-score! (car data)) (set! FLIP-COUNTER (cadr data)) (restore-variables variable-list (caddr data))) ;(register-undo-function undo-func '(score FLIP-COUNTER)) (define (snapshot-board slot-id moving-slot old-cards) (cond ((>= slot-id SLOTS) '()) ((= slot-id moving-slot) (cons old-cards (snapshot-board (+ 1 slot-id) moving-slot old-cards))) (#t (cons (get-cards slot-id) (snapshot-board (+ 1 slot-id) moving-slot old-cards))))) ; called from C: (define (record-move slot-id old-cards) (set! MOVE (list undo-func (list (get-score) FLIP-COUNTER (save-variables variable-list)) (snapshot-board 0 slot-id old-cards)))) ; called from C: (define (discard-move) (set! MOVE '())) ;; Routines for saving/restoring generic variables ; Get a list of values for the variables we wish to save. (define save-variables (lambda (names) (if (equal? '() names) '() (cons (eval-1 (car names)) (save-variables (cdr names)))))) ; Restore all the state variables for a game (define restore-variables (lambda (names values) (or (equal? '() names) (begin (eval-1 (list 'set! (car names) (car values))) (restore-variables (cdr names) (cdr values)) ))))