#lang racket
;;; ゲームの環境情報
(struct world (stones player) #:transparent)
;;; メッセージ分離方式
(define *messages* '((init . "石の数 (10以上) :")
(number-of-stones . "石の数: ~a~%")
(prompt . "プレイヤー~aの番です~%何個取る (1〜3個) ?")
(winner . "プレイヤー~aの勝ち~%")))
;;; 入力の最小値と最大値
(define-values (*min* *max*) (values 0 4))
;;; Eval
(define (world-go x env)
(let ((stones (- (world-stones env) x))
(player (if (= (world-player env) 1)
2
1)))
(world stones player)))
;;;; Eval(短縮版)
;(define (world-go x env)
; (world (- (world-stones env) x)
; (if (= (world-player env) 1)
; 2
; 1)))
;;; Read
(define (input env prompt)
(display (format prompt (world-player env)))
(let ((num (read)))
(if (and (number? num) (> num *min*) (< num *max*))
num
(input env prompt))))
;;; Read-Eval-Print Loop
(define (nim env)
(display (format (cdr (assq 'number-of-stones *messages*))
(world-stones env)))
(if (game-ends? env)
(display (format (cdr (assq 'winner *messages*)) (winner env)))
(nim (world-go (input env (cdr (assq 'prompt *messages*))) env))))
;;; REPL 用補助関数
;; ゲーム終了判定
(define (game-ends? env)
(< (world-stones env) 2))
;; ゲーム勝者表示
(define (winner env)
(let ((player (world-player env)))
(if (zero? (world-stones env))
player
(if (= player 1)
2
1))))
;;; 初期化
(define (init)
(display (cdr (assq 'init *messages*)))
(let ((num (read)))
(if (and (number? num) (> num 9))
(world num 1)
(init))))
;;; ローカル関数使用版
;(define (nim env)
; (define (winner env)
; (let ((player (world-player env)))
; (if (zero? (world-stones env))
; player
; (if (= player 1)
; 2
; 1))))
; (define (game-ends? env)
; (< (world-stones env) 2))
; (let loop ((env env))
; (display (format (cdr (assq 'number-of-stones *messages*))
; (world-stones env)))
; (if (game-ends? env)
; (display (format (cdr (assq 'winner *messages*)) (winner env)))
; (loop (world-go (input env (cdr (assq 'prompt *messages*))) env)))))
;;;; Read(コンピュータが参加する版)
;(define (input env prompt)
; (display (format (cdr (assq 'prompt *messages*)) (world-player env)))
; ;;; ここを改造
; (let ((num (if (= (world-player env) 1)
; (read)
; (let ((result (add1 (random 3))))
; (display result) ;; read の仕様に合わせて、一旦出力して
; (newline) ;; 改行する
; result))))
; (if (and (number? num) (> num *min*) (< num *max*))
; num
; (input env prompt))))
;;; ゲーム実行
(nim (init))