-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgentle_tic_tac_toe.lisp
144 lines (121 loc) · 4.21 KB
/
gentle_tic_tac_toe.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(defparameter *player* 1)
(defparameter *computer* 10)
(defparameter *triplets* '((1 2 3) (4 5 6) (7 8 9)
(1 4 7) (2 5 8) (3 6 9)
(1 5 9) (3 5 7)))
(defun make-board ()
"Initialize the board for the game. 0 is empty, 1 is player, 10 is computer."
(list 'board 0 0 0 0 0 0 0 0 0))
(defun start-game ()
"Start a new tic-tac-toe game."
(if (y-or-n-p "Want to start first? ")
(player-turn (make-board))
(computer-turn (make-board))))
(defun check-game-over (board)
"Check if the game is over and print a message if that is the case."
(when (or (game-won-p board)
(board-full-p board))
(format t "~&Game over!~%")
t))
(defun board-full-p (board)
"Check if the board is full."
(not (member 0 board)))
(defun game-won-p (board)
"Check if player or computer has won the game."
(find-if
#'(lambda (triplet)
(let ((sum (triplet-sum triplet board)))
(or (equal sum 30)
(equal sum 3))))
*triplets*))
(defun triplet-sum (triplet board)
"Returns the sum of the triplet on the board."
(apply
#'+
(mapcar #'(lambda (pos) (get-square pos board)) triplet)))
(defun player-turn (board)
"The player takes a turn."
(print-board board)
(unless (check-game-over board)
(let ((pos (read-valid-pos board)))
(computer-turn (make-move *player* pos board)))))
(defun computer-turn (board)
"The computer takes a turn."
(print-board board)
(unless (check-game-over board)
(let ((move (calculate-move board)))
(format t "~&~A~%" (move-explanation move))
(player-turn
(make-move *computer* (move-pos move) board)))))
(defun move-explanation (move)
"Return the explanation for the move."
(first move))
(defun move-pos (move)
"Return the position for the move."
(second move))
(defun calculate-move (board)
"Calculate a move for the computer with explanation."
(cond ((winning-move *computer* board)
(list "Winning move" (winning-move *computer* board)))
((winning-move *player* board)
(list "Stop player from winning" (winning-move *player* board)))
(t (list "Random move" (random-move board)))))
(defun winning-move (player board)
"Return a winning position for player on board."
(let ((triplet (winning-triplet player board)))
(if triplet
(find-if #'(lambda (pos) (zerop (get-square pos board))) triplet))))
(defun winning-triplet (player board)
"Return a winning triplet for player on board."
(find-if #'(lambda (triplet)
(equal (triplet-sum triplet board) (* 2 player)))
*triplets*))
(defun random-move (board)
"Calculate a random move on the board"
(let ((pos (+ 1 (random 9))))
(if (available-square-p pos board)
pos
(random-move board))))
(defun make-move (player pos board)
"Plays position for player on board."
(setf (nth pos board) player)
board)
(defun read-valid-pos (board)
"Reads a valid position for the current board."
(format t "~&Where are you going to play? ")
(let ((pos (read)))
(cond ((and (numberp pos)
(in-rangep pos 1 9)
(available-square-p pos board)
pos)))))
(defun get-square (pos board)
"Gets the value of a square on the board."
(nth pos board))
(defun available-square-p (pos board)
"Says if the given square on the board is empty."
(zerop (get-square pos board)))
(defun in-rangep (n lower upper)
"Says if n is between lower and upper (inclusive)."
(and (>= n lower)
(<= n upper)))
(defun print-board (board)
"Prints the tic tac toe board."
(print-row (nth 1 board) (nth 2 board) (nth 3 board))
(print-separator)
(print-row (nth 4 board) (nth 5 board) (nth 6 board))
(print-separator)
(print-row (nth 7 board) (nth 8 board) (nth 9 board)))
(defun print-separator ()
"Prints the board horizontal line."
(format t "---+---+---~%"))
(defun print-row (x y z)
"Prints a row of the tic tac toe board."
(format t " ~A | ~A | ~A~%"
(convert-to-letter x)
(convert-to-letter y)
(convert-to-letter z)))
(defun convert-to-letter (p)
"Returns the string representation of p."
(cond ((equal p 0) " ")
((equal p 1) "X")
((equal p 10) "O")))