Tic-tac-toe: Difference between revisions

13,675 bytes added ,  1 month ago
m
Fixed the name of the GitHub repo of the PostScript version
No edit summary
m (Fixed the name of the GitHub repo of the PostScript version)
 
(8 intermediate revisions by 5 users not shown)
Line 4,453:
When the user clicks on the grid, this generates an event that is handled by the program. The mouse click coordinates are converted to a cell coordinates and a large X is placed in the box. At this point program tests if this resulted in a win. Next it calculate the computer's response and it checks to see if the computer won. The process is repeated until there is a winner or a draw.
 
[[File:DelphiTicTacToe.png|frame|none]]
 
 
<syntaxhighlight lang="Delphi">
Line 4,736:
 
</pre>
 
 
=={{header|EasyLang}}==
Line 4,742 ⟶ 4,741:
It uses minimax with alpha-beta pruning. Therefore, the computer never loses.
 
[https://easylang.onlinedev/apps/tictactoe.html Run it]
 
<syntaxhighlight lang="text">
len f[] 9
state = 0
textsize 14
#
funcproc init . .
linewidth 2
clear
Line 4,769 ⟶ 4,768:
.
.
funcproc draw ind . .
c = (ind - 1) mod 3
r = (ind - 1) div 3
Line 4,788 ⟶ 4,787:
.
.
funcproc sum3 a d . st .
for i = 1 to 3
s += f[a]
Line 4,799 ⟶ 4,798:
.
.
funcproc rate . res done .
res = 0
for i = 1 step 3 to 7
call sum3 i 1 res
.
for i = 1 to 3
call sum3 i 3 res
.
call sum3 1 4 res
call sum3 3 2 res
cnt = 1
for i = 1 to 9
Line 4,821 ⟶ 4,820:
.
.
funcproc minmax player alpha beta . rval rmov .
call rate rval done
if done = 1
if player = 1
Line 4,829 ⟶ 4,828:
else
rval = alpha
start = randomrandint 9
mov = start
repeat
if f[mov] = 0
f[mov] = player
call minmax (5 - player) (-beta) (-rval) val h
val = -val
f[mov] = 0
Line 4,847 ⟶ 4,846:
.
.
funcproc show_result val . .
color 555
move 16 4
Line 4,860 ⟶ 4,859:
state += 2
.
funcproc computer . .
call minmax 4 -11 11 val mov
f[mov] = 4
call draw mov
call rate val done
state = 0
if done = 1
call show_result val
.
.
funcproc human . .
mov = floor ((mouse_x - 6) / 28) + 3 * floor ((mouse_y - 16) / 28) + 1
if f[mov] = 0
f[mov] = 1
call draw mov
state = 1
timer 0.5
Line 4,880 ⟶ 4,879:
.
on timer
call rate val done
if done = 1
call show_result val
else
call computer
.
.
Line 4,890 ⟶ 4,889:
if state = 0
if mouse_x > 6 and mouse_x < 90 and mouse_y > 16
call human
.
elif state >= 2
state -= 2
call init
.
.
call init
</syntaxhighlight>
 
Line 8,893 ⟶ 8,892:
Game over. Computer wins!
</pre>
 
=={{header|Koka}}==
Effectful Version
<syntaxhighlight lang="koka">
import std/os/readline
 
fun member(x: a, xs: list<a>, compare: (a, a) -> bool) : bool
match xs
Nil -> False
Cons(y, ys) -> x.compare(y) || member(x, ys,compare)
 
fun member(xs: list<a>, x: a, compare: (a, a) -> bool) : bool
x.member(xs, compare)
 
 
struct coord
x: int
y: int
 
fun show(c: coord) : string {
"(" ++ c.x.show() ++ ", " ++ c.y.show() ++ ")"
}
 
 
fun (==)(c1: coord, c2: coord) : bool
c1.x == c2.x && c1.y == c2.y
 
 
effect ctl eject(): a
 
 
fun parse(str : string, moves : list<coord>) : <console, exn|_e>coord
match str.list()
Cons('0', Cons(' ', Cons('0', Nil))) -> Coord(0,0)
Cons('0', Cons(' ', Cons('1', Nil))) -> Coord(0,1)
Cons('0', Cons(' ', Cons('2', Nil))) -> Coord(0,2)
Cons('1', Cons(' ', Cons('0', Nil))) -> Coord(1,0)
Cons('1', Cons(' ', Cons('1', Nil))) -> Coord(1,1)
Cons('1', Cons(' ', Cons('2', Nil))) -> Coord(1,2)
Cons('2', Cons(' ', Cons('0', Nil))) -> Coord(2,0)
Cons('2', Cons(' ', Cons('1', Nil))) -> Coord(2,1)
Cons('2', Cons(' ', Cons('2', Nil))) -> Coord(2,2)
Cons('0', Cons(',', Cons('0', Nil))) -> Coord(0,0)
Cons('0', Cons(',', Cons('1', Nil))) -> Coord(0,1)
Cons('0', Cons(',', Cons('2', Nil))) -> Coord(0,2)
Cons('1', Cons(',', Cons('0', Nil))) -> Coord(1,0)
Cons('1', Cons(',', Cons('1', Nil))) -> Coord(1,1)
Cons('1', Cons(',', Cons('2', Nil))) -> Coord(1,2)
Cons('2', Cons(',', Cons('0', Nil))) -> Coord(2,0)
Cons('2', Cons(',', Cons('1', Nil))) -> Coord(2,1)
Cons('2', Cons(',', Cons('2', Nil))) -> Coord(2,2)
Cons('q', Nil) -> eject()
_ ->
println("Invalid move, please try again")
gen_move(moves)
 
fun gen_move(moves : list<coord>) : <console, exn|_e> coord
val move : coord = parse(readline(), moves)
 
if moves.any() fn (c : coord) { c == move } then {
println("Invalid move, please try again")
gen_move(moves)
} else {
move
}
 
fun create-board() : list<list<char>> {
[['.','.','.'],['.','.','.'],['.','.','.']]
}
 
fun show(grid: list<list<char>>) : string
var line := 0
grid.foldl(" 0 1 2\n") fn(acc, row: list<char>)
val out = row.foldl(acc ++ line.show() ++ " ") fn(acc, col: char)
acc ++ col.string() ++ " "
++ "\n"
line := line + 1
out
 
fun get_board_position(board : list<list<char>>, coord : coord) : maybe<char> {
match board[coord.y] {
Nothing -> Nothing
Just(row) -> row[coord.x]
}
}
fun mark_board(board: list<list<char>>,coord: coord, mark: char): maybe<list<list<char>>>
val new_row: maybe<list<char>> = match board[coord.y]
Nothing -> Nothing
Just(row) -> Just(row.take(coord.x) ++ mark.Cons(row.drop(coord.x + 1)))
match new_row
Nothing -> Nothing
Just(row) -> Just(board.take(coord.y) ++ row.Cons(board.drop(coord.y + 1)))
 
 
effect ctl not_full() : ()
 
fun check_full(board: list<list<char>>) : bool
fun helper()
var full := True
board.foreach() fn(row)
if '.'.member(row) fn (a, b) a == b then {
not_full()
}
full
with ctl not_full() False
helper()
 
fun check_win(board: list<list<char>>, mark: char) : <div, exn|_e>bool
var win := False
var i := 0
while {i < 3} {
if board.get_board_position(Coord(i,0)).unwrap() == mark && board.get_board_position(Coord(i,1)).unwrap() == mark && board.get_board_position(Coord(i,2)).unwrap() == mark then {
win := True
}
if board.get_board_position(Coord(0,i)).unwrap() == mark && board.get_board_position(Coord(1,i)).unwrap() == mark && board.get_board_position(Coord(2,i)).unwrap() == mark then {
win := True
}
i := i + 1
}
if board.get_board_position(Coord(0,0)).unwrap() == mark && board.get_board_position(Coord(1,1)).unwrap() == mark && board.get_board_position(Coord(2,2)).unwrap() == mark then {
win := True
}
if board.get_board_position(Coord(0,2)).unwrap() == mark && board.get_board_position(Coord(1,1)).unwrap() == mark && board.get_board_position(Coord(2,0)).unwrap() == mark then {
win := True
}
win
 
 
 
fun human_logic(board: list<list<char>>, moves: list<coord>, mark: char, other_mark: char) : <console,div,exn|_e> coord
gen_move(moves)
 
fun ai_logic(board: list<list<char>>, moves: list<coord>, mark: char, other_mark: char)
board.gen_ai_move(moves,mark, other_mark)
 
 
struct move
move : coord
score: int
 
fun (==)(m1 : move, m2 : move) : bool {
m1.move == m2.move && m1.score == m2.score
}
 
fun eval_board(board : list<list<char>>, mark: char, other-mark : char) {
if board.check_win(mark) then {
10
}
elif board.check_win(other-mark) then {
-10
}
else {
0
}
}
 
fun maximum-move(a : move, b : move) : move {
if a.score > b.score then {
a
}
else {
b
}
}
 
 
fun gen_ai_move(board : list<list<char>>, moves : list<coord>, mark : char, other-mark : char) {
val best_move : move = Move(Coord(-1,-1), -1000)
[0,1,2].foldl(best_move) fn (bstMove : move, i : int) {
[0,1,2].foldl(bstMove) fn (bstMve : move, j : int) {
if Coord(i,j).member(moves) fn (a,b) {a == b} then {
bstMve
}
else {
val new_board : list<list<char>> = board.mark_board(Coord(i,j), mark).unwrap()
val new-max = maximum-move(bstMve, Move(Coord(i,j), new_board.minimax(moves ++ [Coord(i,j)], 0, False, mark, other-mark)))
new-max
}
}
}.move
}
 
fun unwrap(x: maybe<a>): <exn> a
match x
Just(a) -> a
Nothing -> throw("value was Nothing")
 
// A basic implementation of Minimax
// This uses brace style to show that it is possible
fun minimax(board : list<list<char>>, moves: list<coord>, depth : int, isMaximizingPlayer : bool, mark : char, other-mark : char) : <div,exn|_e> int {
val score : int = board.eval_board(mark, other-mark)
if score == 10 then {
score
}
elif score == -10 then {
score
}
elif board.check_full() then {
0
}
else {
if isMaximizingPlayer then {
val bestVal: int = -1000
[0,1,2].foldl(bestVal) fn (bstVal : int, i : int) {
[0,1,2].foldl(bstVal) fn (bstVl : int, j : int) {
if Coord(i,j).member(moves) fn(a, b) {a == b} then {
bstVl
}
else {
val new_board : list<list<char>> = board.mark_board(Coord(i,j), mark).unwrap()
val value : int = new_board.minimax(moves ++ [Coord(i,j)], depth + 1, !isMaximizingPlayer, mark, other-mark)
max(bstVl, value)
}
}
}
}
else {
val bestVal: int = 1000
[0,1,2].foldl(bestVal) fn (bstVal : int, i : int) {
[0,1,2].foldl(bstVal) fn (bstVl : int, j : int) {
if Coord(i,j).member(moves) fn(a,b) {a == b} then {
bstVl
}
else {
val new_board : list<list<char>> = board.mark_board(Coord(i,j), other-mark).unwrap()
val value : int = new_board.minimax(moves ++ [Coord(i,j)], depth + 1, !isMaximizingPlayer, mark, other-mark)
min(bstVl, value)
}
}
}
}
}
}
 
// The main business logic of the entire game
// This function checks if there is a draw or a win
fun play_game()
val board = get_board()
if board.check_full() then
println("Draw!")
println("Final board:")
board.show().println
else
"Next Turn:".println
board.show().println
val current_mark = get_current_mark()
val other_mark = get_other_mark()
play_turn(current_mark, other_mark)
val new_board = get_board()
if new_board.check_win(current_mark) then
println("Player " ++ current_mark.show() ++ " wins!")
println("Final board:")
new_board.show().println
else
flip()
play_game()
 
 
effect human_turn
fun play_human_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord
effect ai_turn
fun play_ai_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord
 
effect player1
fun player1_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord
fun get_player1_mark() : char
effect player2
fun player2_turn(pair: (list<list<char>>, list<coord>), marks: (char, char)) : coord
fun get_player2_mark() : char
 
 
effect turn
// Changes the current player to a different one
fun flip() : ()
// Calls the appropriate code for the current player
fun play_turn(mark: char, other_mark: char) : ()
// Gets the symbol of the active player
fun get_current_mark(): char
// Gets the symbol of the inactive player
fun get_other_mark(): char
// This allows us to access the board
fun get_board() : list<list<char>>
// This allows us to access the moves that have been made
fun get_moves(): list<coord>
 
 
 
type player
Human
AI
 
type players
One
Two
 
// This function encapsulates the state of the entire game
// Think of it as calling a method on an object but with pure functions
fun initialize_and_start_game(game_type: int)
var current_player := One
var current_board := create-board()
var all_moves := []
 
var player1 := Human
var player2 := AI
 
match game_type
1 -> {
player1 := Human
player2 := Human
}
2 -> {
player1 := Human
player2 := AI
}
3 -> {
player1 := AI
player2 := AI
}
_ -> throw("invalid game type")
 
with handler
ctl eject()
println("Have a nice day.")
with fun play_human_turn(pair: (list<list<char>>, list<coord>), marks: (char, char))
human_logic(pair.fst,pair.snd,marks.fst,marks.snd)
with fun play_ai_turn(pair: (list<list<char>>, list<coord>), marks: (char, char))
ai_logic(pair.fst,pair.snd,marks.fst,marks.snd)
with handler
fun player1_turn(pair: (list<list<char>>, list<coord>), marks: (char, char))
match player1
Human -> play_human_turn(pair, marks)
AI -> play_ai_turn(pair, marks)
fun get_player1_mark()
'X'
with handler
fun player2_turn(pair: (list<list<char>>, list<coord>), marks: (char, char))
match player2
Human -> play_human_turn(pair, marks)
AI -> play_ai_turn(pair, marks)
fun get_player2_mark()
'O'
with handler
return(x) ()
fun flip()
match current_player
One -> current_player := Two
Two -> current_player := One
fun play_turn(mark: char, other_mark: char)
match current_player
One -> {
val coord = player1_turn((current_board, all_moves), (mark, other_mark))
current_board := mark_board(current_board,coord,get_player1_mark()).unwrap
all_moves := Cons(coord, all_moves)
()
}
Two -> {
val coord = player2_turn((current_board, all_moves), (mark, other_mark))
 
current_board := mark_board(current_board,coord,get_player2_mark()).unwrap
all_moves := Cons(coord, all_moves)
()
}
fun get_current_mark() match current_player
One -> get_player1_mark()
Two -> get_player2_mark()
fun get_other_mark() match current_player
Two -> get_player1_mark()
One -> get_player2_mark()
fun get_board() current_board
fun get_moves() all_moves
play_game()
 
 
fun prompt_game_type()
match readline().list()
Cons('1', Nil) -> 1
Cons('2', Nil) -> 2
Cons('3', Nil) -> 3
_ ->
println("Invalid game mode, please try again")
prompt_game_type()
 
 
fun main ()
println("Welcome to Tic Tac Toe!")
println("Please select a game mode:")
println("1. Two player")
println("2. One player")
println("3. Zero player")
println("Enter the number of the game mode you want to play")
 
val game_type = prompt_game_type()
 
"Enter your input as 'x y' or 'x,y' when selecting a spot".println
 
initialize_and_start_game(game_type)
</syntaxhighlight>
 
=={{header|Kotlin}}==
Line 11,674 ⟶ 12,085:
+---+---+---+
a b c</pre>
 
=={{header|PostScript}}==
<syntaxhighlight lang="postscript">
%!PS
%
% Play Tic-Tac-Toe against your printer
% 2024-04 Nicolas Seriot https://github.com/nst/PSTicTacToe
%
% On GhostScript:
% gs -DNOSAFER -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile="%d.pdf" ttt.ps
%
% On a real PostScript printer:
% cat ttt.ps - | nc 192.168.2.10 9100
 
<</PageSize[595 842]>>setpagedevice/Courier findfont 48 scalefont setfont/b
(.........)def/f{/n 0 def b{46 eq{/n n 1 add def}if}forall n}def/w{/c exch def
[(O)(X)]{/p exch def[[0 1 2][3 4 5][6 7 8][0 3 6][1 4 7][2 5 8][0 4 8][2 4 6]]{
/t exch def/o true def t{/pos exch def/o c pos 1 getinterval p eq o and def}
forall o{exit}if}forall o{exit}if}forall o}def/g{/s exch def/m null def f 0 eq{
/m(TIE)def}if b w{/m(XXXXXXX WINS)def m 0 s putinterval}if()= 0 3 6{b exch 3
getinterval =}for()= m null ne{m =}if 4 setlinewidth 200 700 moveto 200 400
lineto 300 700 moveto 300 400 lineto 100 600 moveto 400 600 lineto 100 500
moveto 400 500 lineto stroke 0 1 b length 1 sub{/i exch def b i 1 getinterval
(.)ne{gsave 0 0 moveto 100 i 3 mod 100 mul add 35 add 700 i 3 idiv 100 mul sub
65 sub moveto b i 1 getinterval show grestore}if}for m null ne{100 300 moveto m
show}if showpage m null ne{quit}if}def{/d false def 0 1 8{/i exch def/e b dup
length string cvs def e i 1 getinterval(.)eq{[(X)(O)]{/p exch def e i p
putinterval e w{b i(X)putinterval/d true def exit}if}forall}if d{exit}if}for d
not{/x rand f mod def/c 0 def 0 1 b length 1 sub{/i exch def b i 1 getinterval
(.)eq{c x eq{b i(X)putinterval exit}if/c c 1 add def}if}for}if(PRINTER)g b{
(human turn (1-9) >)print flush(%lineedit)(r)file( )readline pop dup length
0 gt{0 1 getinterval}{pop( )}ifelse/o exch def(123456789)o search{pop pop pop b
o cvi 1 sub 1 getinterval(.)eq{o cvi 1 sub exit}if}{pop}ifelse(bad input) ==}
loop(O)putinterval(HUMAN )g}loop
</syntaxhighlight>
 
=={{header|Prolog}}==
Line 15,535 ⟶ 15,981:
{{trans|Kotlin}}
{{libheader|Wren-ioutil}}
<syntaxhighlight lang="ecmascriptwren">import "random" for Random
import "./ioutil" for Input
 
var r = Random.new()
2

edits