June 2018 Update
This commit is contained in:
parent
ba8067c3b7
commit
22f33d4004
5278 changed files with 84726 additions and 14379 deletions
|
|
@ -1,9 +1,12 @@
|
|||
[[File:Tic_tac_toe.jpg|500px||right]]
|
||||
|
||||
;Task:
|
||||
Play a game of [[wp:Tic-tac-toe|tic-tac-toe]]. Ensure that legal moves are played and that a winning position is notified.
|
||||
Play a game of [[wp:Tic-tac-toe|tic-tac-toe]].
|
||||
|
||||
Ensure that legal moves are played and that a winning position is notified.
|
||||
|
||||
|
||||
;See also
|
||||
* [http://mathworld.wolfram.com/Tic-Tac-Toe.html Mathworld™, Tic-Tac-Toe game].
|
||||
* [http://mathworld.wolfram.com/Tic-Tac-Toe.html MathWorld™, Tic-Tac-Toe game].
|
||||
* [https://en.wikipedia.org/wiki/Tic-tac-toe Wikipedia tic-tac-toe].
|
||||
<br><br>
|
||||
|
|
|
|||
99
Task/Tic-tac-toe/Fortran/tic-tac-toe.f
Normal file
99
Task/Tic-tac-toe/Fortran/tic-tac-toe.f
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
! This is a fortran95 implementation of the game of tic-tac-toe.
|
||||
! - Objective was to use less than 100 lines.
|
||||
! - No attention has been devoted to efficiency.
|
||||
! - Indentation by findent: https://sourceforge.net/projects/findent/
|
||||
! - This is free software, use as you like at own risk.
|
||||
! - Compile: gfortran -o tictactoe tictactoe.f90
|
||||
! - Run: ./tictactoe
|
||||
! Comments to: wvermin at gmail dot com
|
||||
module tic
|
||||
implicit none
|
||||
integer :: b(9)
|
||||
contains
|
||||
logical function iswin(p)
|
||||
integer,intent(in) :: p
|
||||
iswin = &
|
||||
all(b([1,2,3])==p).or.all(b([4,5,6])==p).or.all(b([7,8,9])==p).or.&
|
||||
all(b([1,4,7])==p).or.all(b([2,5,8])==p).or.all(b([3,6,9])==p).or.&
|
||||
all(b([1,5,9])==p).or.all(b([3,5,7])==p)
|
||||
end function iswin
|
||||
subroutine printb(mes)
|
||||
character(len=*) :: mes
|
||||
integer :: i,j
|
||||
character :: s(0:2) = ['.','X','O']
|
||||
print "(3a3,' ',3i3)",(s(b(3*i+1:3*i+3)),(j,j=3*i+1,3*i+3),i=0,2)
|
||||
if(mes /= ' ') print "(/,a)",mes
|
||||
end subroutine printb
|
||||
integer recursive function minmax(player,bestm) result(bestv)
|
||||
integer :: player,bestm,move,v,bm,win=1000,inf=100000
|
||||
real :: x
|
||||
if (all(b .ne. 0)) then
|
||||
bestv = 0
|
||||
else
|
||||
bestv = -inf
|
||||
do move=1,9
|
||||
if (b(move) == 0) then
|
||||
b(move) = player
|
||||
if (iswin(player)) then
|
||||
v = win
|
||||
else
|
||||
call random_number(x)
|
||||
v = -minmax(3-player,bm) - int(10*x)
|
||||
endif
|
||||
if (v > bestv) then
|
||||
bestv = v
|
||||
bestm = move
|
||||
endif
|
||||
b(move) = 0
|
||||
if (v == win) exit
|
||||
endif
|
||||
enddo
|
||||
endif
|
||||
end function minmax
|
||||
end module tic
|
||||
program tictactoe
|
||||
! computer: player=1, user: player=2
|
||||
use tic
|
||||
implicit none
|
||||
integer :: move,ios,v,bestmove,ply,player=2,k,values(8)
|
||||
integer,allocatable :: seed(:)
|
||||
call date_and_time(values=values)
|
||||
call random_seed(size=k)
|
||||
allocate(seed(k))
|
||||
seed = values(8)+1000*values(7)+60*1000*values(6)+60*60*1000*values(5)
|
||||
call random_seed(put=seed)
|
||||
mainloop: do
|
||||
b = 0
|
||||
call printb('You have O, I have X. You enter 0: game ends.')
|
||||
plyloop: do ply=0,4
|
||||
if (player == 2 .or. ply >0 ) then ! user move
|
||||
write(*,"(/,a)",advance='no'),'Your move? (0..9) '
|
||||
getloop: do
|
||||
readloop: do
|
||||
read (*,*,iostat=ios),move
|
||||
if (ios == 0 .and. move >= 0 .and. move <= 9) exit readloop
|
||||
write(*,"(a)",advance='no'),'huh? Try again (0..9): '
|
||||
enddo readloop
|
||||
if ( move == 0) exit mainloop
|
||||
if (b(move) == 0) exit getloop
|
||||
write(*,"(a)",advance='no'),'Already occupied, again (0..9): '
|
||||
enddo getloop
|
||||
b(move) = 2
|
||||
if(iswin(2)) then ! this should not happen
|
||||
call printb('***** You win *****')
|
||||
exit plyloop
|
||||
endif
|
||||
endif
|
||||
v = minmax(1,bestmove) ! computer move
|
||||
b(bestmove) = 1
|
||||
if(iswin(1)) then
|
||||
call printb('***** I win *****')
|
||||
exit plyloop
|
||||
endif
|
||||
write(*,"(/,a,i3)"), 'My move: ',bestmove
|
||||
call printb(' ')
|
||||
enddo plyloop
|
||||
if(ply == 5) write(*,"('***** Draw *****',/)")
|
||||
player = 3-player
|
||||
enddo mainloop
|
||||
end program
|
||||
107
Task/Tic-tac-toe/Kotlin/tic-tac-toe.kotlin
Normal file
107
Task/Tic-tac-toe/Kotlin/tic-tac-toe.kotlin
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// version 1.1.51
|
||||
|
||||
import java.util.Random
|
||||
|
||||
val r = Random()
|
||||
val b = Array(3) { IntArray(3) } // board -> 0: blank; -1: computer; 1: human
|
||||
|
||||
var bestI = 0
|
||||
var bestJ = 0
|
||||
|
||||
fun checkWinner(): Int {
|
||||
for (i in 0..2) {
|
||||
if (b[i][0] != 0 && b[i][1] == b[i][0] && b[i][2] == b[i][0]) return b[i][0]
|
||||
if (b[0][i] != 0 && b[1][i] == b[0][i] && b[2][i] == b[0][i]) return b[0][i]
|
||||
}
|
||||
if (b[1][1] == 0) return 0
|
||||
if (b[1][1] == b[0][0] && b[2][2] == b[0][0]) return b[0][0]
|
||||
if (b[1][1] == b[2][0] && b[0][2] == b[1][1]) return b[1][1]
|
||||
return 0
|
||||
}
|
||||
|
||||
fun showBoard() {
|
||||
val t = "X O"
|
||||
for (i in 0..2) {
|
||||
for (j in 0..2) print("${t[b[i][j] + 1]} ")
|
||||
println()
|
||||
}
|
||||
println("-----")
|
||||
}
|
||||
|
||||
fun testMove(value: Int, depth: Int): Int {
|
||||
var best = -1
|
||||
var changed = 0
|
||||
var score = checkWinner()
|
||||
if (score != 0) return if (score == value) 1 else -1
|
||||
for (i in 0..2) {
|
||||
for (j in 0..2) {
|
||||
if (b[i][j] != 0) continue
|
||||
b[i][j] = value
|
||||
changed = value
|
||||
score = -testMove(-value, depth + 1)
|
||||
b[i][j] = 0
|
||||
if (score <= best) continue
|
||||
if (depth == 0) {
|
||||
bestI = i
|
||||
bestJ = j
|
||||
}
|
||||
best = score
|
||||
}
|
||||
}
|
||||
return if (changed != 0) best else 0
|
||||
}
|
||||
|
||||
fun game(user: Boolean): String {
|
||||
var u = user
|
||||
for (i in 0..2) b[i].fill(0)
|
||||
print("Board postions are numbered so:\n1 2 3\n4 5 6\n7 8 9\n")
|
||||
print("You have O, I have X.\n\n")
|
||||
|
||||
for (k in 0..8) {
|
||||
while (u) {
|
||||
var move: Int?
|
||||
do {
|
||||
print("Your move: ")
|
||||
move = readLine()!!.toIntOrNull()
|
||||
}
|
||||
while (move != null && move !in 1..9)
|
||||
move = move!! - 1
|
||||
val i = move / 3
|
||||
val j = move % 3
|
||||
if (b[i][j] != 0) continue
|
||||
b[i][j] = 1
|
||||
break
|
||||
}
|
||||
if (!u) {
|
||||
if (k == 0) { // randomize if computer opens, less boring
|
||||
bestI = r.nextInt(Int.MAX_VALUE) % 3
|
||||
bestJ = r.nextInt(Int.MAX_VALUE) % 3
|
||||
}
|
||||
else testMove(-1, 0)
|
||||
b[bestI][bestJ] = -1
|
||||
val myMove = bestI * 3 + bestJ + 1
|
||||
println("My move: $myMove")
|
||||
}
|
||||
showBoard()
|
||||
val win = checkWinner()
|
||||
if (win != 0) return (if (win == 1) "You win" else "I win") + ".\n\n"
|
||||
u = !u
|
||||
}
|
||||
return "A draw.\n\n"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
var user = false
|
||||
while (true) {
|
||||
user = !user
|
||||
print(game(user))
|
||||
var yn: String
|
||||
do {
|
||||
print("Play again y/n: ")
|
||||
yn = readLine()!!.toLowerCase()
|
||||
}
|
||||
while (yn != "y" && yn != "n")
|
||||
if (yn != "y") return
|
||||
println()
|
||||
}
|
||||
}
|
||||
237
Task/Tic-tac-toe/Lingo/tic-tac-toe.lingo
Normal file
237
Task/Tic-tac-toe/Lingo/tic-tac-toe.lingo
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
global $ -- object representing simple framework
|
||||
global gBoard -- current board image
|
||||
global gBoardTemplate -- empty board image
|
||||
global gHumanChip -- cross image
|
||||
global gComputerChip -- circle image
|
||||
global gM -- 3x3 matrix storing game state: 0=free cell, 1=human cell, -1=computer cell
|
||||
global gStep -- index of current move (1..9)
|
||||
global gGameOverFlag -- TRUE if current game is over
|
||||
|
||||
----------------------------------------
|
||||
-- Entry point
|
||||
----------------------------------------
|
||||
on startMovie
|
||||
|
||||
-- libs
|
||||
$.import("sprite")
|
||||
|
||||
-- window properties
|
||||
_movie.stage.title = "Tic-Tac-Toe"
|
||||
_movie.stage.rect = rect(0, 0, 224, 310)
|
||||
_movie.centerStage = TRUE
|
||||
|
||||
-- load images from filesystem
|
||||
m = new(#bitmap)
|
||||
m.importFileInto($.@("resources/cross.bmp"), [#trimWhiteSpace:FALSE])
|
||||
gHumanChip = m.image
|
||||
|
||||
m = new(#bitmap)
|
||||
m.importFileInto($.@("resources/circle.bmp"), [#trimWhiteSpace:FALSE])
|
||||
gComputerChip = m.image
|
||||
|
||||
-- create GUI
|
||||
m = new(#bitmap)
|
||||
m.importFileInto($.@("resources/board.bmp"))
|
||||
m.regpoint = point(0, 0)
|
||||
s = $.sprite.make(m, [#loc:point(20, 20)], TRUE)
|
||||
s.addListener(#mouseDown, _movie, #humanMove)
|
||||
|
||||
gBoard = m.image
|
||||
gBoardTemplate = gBoard.duplicate()
|
||||
|
||||
m = $.sprite.newMember(#button, [#text:"New Game (Human starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
|
||||
s = $.sprite.make(m, [#loc:point(20, 220)], TRUE)
|
||||
s.addListener(#mouseDown, _movie, #newGame, 1)
|
||||
|
||||
m = $.sprite.newMember(#button, [#text:"New Game (Computer starts)", #fontstyle:"bold", #rect:rect(0, 0, 180, 0)])
|
||||
s = $.sprite.make(m, [#loc:point(20, 250)], TRUE)
|
||||
s.addListener(#mouseDown, _movie, #newGame, -1)
|
||||
|
||||
m = $.sprite.newMember(#field, [#name:"feedback", #editable:FALSE, #fontstyle:"bold", #alignment:"center",\
|
||||
#border:0, #color:rgb(255, 0, 0), #rect:rect(0, 0, 180, 0)])
|
||||
s = $.sprite.make(m, [#loc:point(20, 280)], TRUE)
|
||||
|
||||
newGame(1)
|
||||
|
||||
-- show the application window
|
||||
_movie.updateStage()
|
||||
_movie.stage.visible = TRUE
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Starts a new game
|
||||
----------------------------------------
|
||||
on newGame (whoStarts)
|
||||
-- reset board
|
||||
gBoard.copyPixels(gBoardTemplate, gBoardTemplate.rect, gBoardTemplate.rect)
|
||||
-- clear feedback
|
||||
member("feedback").text = ""
|
||||
-- reset states
|
||||
gM = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
|
||||
gStep = 0
|
||||
gGameOverFlag = FALSE
|
||||
if whoStarts=-1 then computerMove()
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Handles a human move (mouse click)
|
||||
----------------------------------------
|
||||
on humanMove ()
|
||||
if gGameOverFlag then return
|
||||
-- find cell for mouse position
|
||||
p = _mouse.clickLoc - sprite(1).loc
|
||||
if p.locH mod 60<4 or p.locV mod 60<4 then return
|
||||
p = p / 60
|
||||
x = p[1] + 1
|
||||
y = p[2] + 1
|
||||
if gM[x][y] then return -- ignore illegal moves
|
||||
gM[x][y] = 1
|
||||
-- update cell image
|
||||
p = p * 60
|
||||
gBoard.copyPixels(gHumanChip, gHumanChip.rect.offset(4+p[1], 4+p[2]), gHumanChip.rect)
|
||||
-- proceed (unless game over)
|
||||
gStep = gStep + 1
|
||||
if not checkHumanMove(x, y) then computerMove()
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Checks if human has won or game ended with draw
|
||||
----------------------------------------
|
||||
on checkHumanMove (x, y)
|
||||
if sum([gM[x][1], gM[x][2], gM[x][3]])=3 then return gameOver(1, [[x, 1], [x, 2], [x, 3]])
|
||||
if sum([gM[1][y], gM[2][y], gM[3][y]])=3 then return gameOver(1, [[1, y], [2, y], [3, y]])
|
||||
if x=y and sum([gM[1][1], gM[2][2], gM[3][3]])=3 then return gameOver(1, [[1, 1], [2, 2], [3, 3]])
|
||||
if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]])=3 then return gameOver(1, [[1, 3], [2, 2], [3, 1]])
|
||||
if gStep=9 then return gameOver(0)
|
||||
return FALSE
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Checks if selecting specified empty cell makes computer or human win
|
||||
----------------------------------------
|
||||
on checkCellWins (x, y, who)
|
||||
wins = who*2
|
||||
if sum([gM[1][y], gM[2][y], gM[3][y]]) = wins then return [[1, y], [2, y], [3, y]]
|
||||
if sum([gM[x][1], gM[x][2], gM[x][3]]) = wins then return [[x, 1], [x, 2], [x, 3]]
|
||||
if x=y and sum([gM[1][1], gM[2][2], gM[3][3]]) = wins then return [[1, 1], [2, 2], [3, 3]]
|
||||
if x+y=4 and sum([gM[1][3], gM[2][2], gM[3][1]]) = wins then return [[1, 3], [2, 2], [3, 1]]
|
||||
return FALSE
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Handles game over
|
||||
----------------------------------------
|
||||
on gameOver (winner, cells)
|
||||
gGameOverFlag = TRUE
|
||||
if winner = 0 then
|
||||
member("feedback").text = "It's a draw!"
|
||||
else
|
||||
-- hilite winning line with yellow
|
||||
img = image(56, 56, 32)
|
||||
img.fill(img.rect, rgb(255, 255, 0))
|
||||
repeat with c in cells
|
||||
x = (c[1]-1)*60 + 4
|
||||
y = (c[2]-1)*60 + 4
|
||||
gBoard.copyPixels(img, img.rect.offset(x, y), img.rect, [#ink:#darkest])
|
||||
end repeat
|
||||
member("feedback").text = ["Human", "Computer"][1+(winner=-1)] & " has won!"
|
||||
end if
|
||||
return TRUE
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Calculates next computer move
|
||||
----------------------------------------
|
||||
on computerMove ()
|
||||
gStep = gStep + 1
|
||||
|
||||
-- move 1: select center
|
||||
if gStep=1 then return doComputerMove(2, 2)
|
||||
|
||||
-- move 2 (human started)
|
||||
if gStep=2 then
|
||||
if gM[2][2]=1 then
|
||||
-- if center, select arbitrary corner
|
||||
return doComputerMove(1, 1)
|
||||
else
|
||||
-- otherwise select center
|
||||
return doComputerMove(2, 2)
|
||||
end if
|
||||
end if
|
||||
|
||||
-- move 3 (computer started)
|
||||
if gStep=3 then
|
||||
-- if corner, select diagonally opposite corner
|
||||
if gM[1][1]=1 then return doComputerMove(3, 3)
|
||||
if gM[3][3]=1 then return doComputerMove(1, 1)
|
||||
if gM[1][3]=1 then return doComputerMove(3, 1)
|
||||
return doComputerMove(1, 1) -- top left corner as default
|
||||
end if
|
||||
|
||||
-- get free cells
|
||||
free = []
|
||||
repeat with x = 1 to 3
|
||||
repeat with y = 1 to 3
|
||||
if gM[x][y]=0 then free.add([x, y])
|
||||
end repeat
|
||||
end repeat
|
||||
|
||||
-- check if computer can win now
|
||||
repeat with c in free
|
||||
res = checkCellWins(c[1], c[2], -1)
|
||||
if res<>FALSE then
|
||||
doComputerMove(c[1], c[2])
|
||||
return gameOver(-1, res)
|
||||
end if
|
||||
end repeat
|
||||
|
||||
-- check if human could win with next move (if yes, prevent it)
|
||||
repeat with c in free
|
||||
res = checkCellWins(c[1], c[2], 1)
|
||||
if res<>FALSE then return doComputerMove(c[1], c[2], TRUE)
|
||||
end repeat
|
||||
|
||||
-- move 4 (human started): prevent "double mills"
|
||||
if gStep=4 then
|
||||
if gM[2][2]=1 and (gM[1][1]=1 or gM[3][3]=1) then return doComputerMove(3, 1)
|
||||
if gM[2][2]=1 and (gM[1][3]=1 or gM[3][1]=1) then return doComputerMove(1, 1)
|
||||
if gM[2][3]+gM[3][2]=2 then return doComputerMove(3, 3)
|
||||
if gM[1][2]+gM[2][3]=2 then return doComputerMove(1, 3)
|
||||
if gM[1][2]+gM[2][1]=2 then return doComputerMove(1, 1)
|
||||
if gM[2][1]+gM[3][2]=2 then return doComputerMove(3, 1)
|
||||
if (gM[1][3]+gM[3][1]=2) or (gM[1][1]+gM[3][3]=2) then return doComputerMove(2, 1)
|
||||
end if
|
||||
|
||||
-- move 5 (computer started): try to create a "double mill"
|
||||
if gStep=5 then
|
||||
repeat with x = 1 to 3
|
||||
col = [gM[x][1], gM[x][2], gM[x][3]]
|
||||
if not (sum(col)=-1 and max(col)=0) then next repeat
|
||||
repeat with y = 1 to 3
|
||||
row = [gM[1][y], gM[2][y], gM[3][y]]
|
||||
if not (sum(row)=-1 and max(row)=0 and gM[x][y]=0) then next repeat
|
||||
return doComputerMove(x, y)
|
||||
end repeat
|
||||
end repeat
|
||||
end if
|
||||
|
||||
-- otherwise use first free cell
|
||||
c = free[1]
|
||||
doComputerMove(c[1], c[2])
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Updates state matrix and cell image
|
||||
----------------------------------------
|
||||
on doComputerMove (x, y, checkDraw)
|
||||
gM[x][y] = -1
|
||||
gBoard.copyPixels(gComputerChip, gComputerChip.rect.offset(4+(x-1)*60, 4+(y-1)*60), gComputerChip.rect)
|
||||
if checkDraw and gStep=9 then gameOver(0)
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
--
|
||||
----------------------------------------
|
||||
on sum (aLine)
|
||||
return aLine[1]+aLine[2]+aLine[3]
|
||||
end
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
use v6;
|
||||
|
||||
my @board = 1..9;
|
||||
my @winning-positions = [0..2], [3..5], [6..8], [0,3,6], [1,4,7], [2,5,8],
|
||||
[0,4,8], [6,4,2];
|
||||
|
||||
sub get-winner() {
|
||||
for @winning-positions {
|
||||
return (@board[$_][0], $_) if [eq] @board[$_];
|
||||
return (@board[|$_][0], $_) if [eq] @board[|$_];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -22,12 +20,13 @@ sub ai-move() {
|
|||
}
|
||||
|
||||
sub print-board() {
|
||||
say @board.map({ "$^a | $^b | $^c" }).join("\n--+---+--\n"), "\n";
|
||||
print "\e[2J";
|
||||
say @board.map({ "$^a | $^b | $^c" }).join("\n--+---+--\n"), "\n";
|
||||
}
|
||||
|
||||
sub human-move() {
|
||||
my $pos = prompt "Choose one of { (free-indexes >>+>> 1).join(",") }: ";
|
||||
if $pos eq any(free-indexes >>+>> 1) {
|
||||
my $pos = prompt "Choose one of { (free-indexes() »+» 1).join(",") }: ";
|
||||
if $pos eq any(free-indexes() »+» 1) {
|
||||
@board[$pos - 1] = 'x';
|
||||
} else {
|
||||
say "Sorry, you want to put your 'x' where?";
|
||||
|
|
@ -35,14 +34,14 @@ sub human-move() {
|
|||
}
|
||||
}
|
||||
|
||||
for (&ai-move, &human-move) xx * {
|
||||
for flat (&ai-move, &human-move) xx * {
|
||||
print-board;
|
||||
.();
|
||||
last if get-winner() or not free-indexes;
|
||||
last if get-winner() or not free-indexes;
|
||||
.();
|
||||
}
|
||||
|
||||
if get-winner() -> ($player, $across) {
|
||||
say "$player wins across [", ($across >>+>> 1).join(", "), "].";
|
||||
say "$player wins across [", ($across »+» 1).join(", "), "].";
|
||||
} else {
|
||||
say "How boring, a draw!";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,111 +1,143 @@
|
|||
/*REXX program plays (with a human) the tic─tac─toe game on an NxN grid. */
|
||||
$=copies('─', 9) /*eyecatcher literal for error messages*/
|
||||
oops =$ '***error*** '; cell@ ="cell number" /*a couple of literals for some SAYs. */
|
||||
sing='│─┼'; jam="║"; bar='═'; junc="╬"; dbl=jam || bar || junc
|
||||
sw=linesize() - 1 /*obtain width of the terminal (less 1)*/
|
||||
$=copies('─', 9) /*eyecatcher for error messages, prompt*/
|
||||
oops = $ '***error*** ' /*literal for when an error happens. */
|
||||
single = '│─┼'; jam= "║"; bar= '═'; junc= "╬"; dbl=jam || bar || junc
|
||||
sw = linesize() - 1 /*obtain width of the terminal (less 1)*/
|
||||
parse arg N hm cm .,@. /*obtain optional arguments from the CL*/
|
||||
if N=='' | N=="," then N=3; oN=N /*N not specified? Then use default.*/
|
||||
N=abs(N); NN=N*N; middle=NN%2+N%2 /*if N < 0, then computer goes first. */
|
||||
if N<2 then do; say oops 'tic─tac─toe grid is too small: ' N; exit; end
|
||||
pad=left('', sw%NN) /*display padding: 6x6 in 80 columns.*/
|
||||
if hm=='' then hm="X"; if cm=='' then cm="O" /*define the markers: Human, computer*/
|
||||
hm=aChar(hm,'human'); cm=aChar(cm,'computer') /*process/define markers for players. */
|
||||
parse upper value hm cm with uh uc /*use uppercase values is markers: X x*/
|
||||
N = abs(N) /*if N < 0. then computer goes first. */
|
||||
NN = N*N /*calculate the square of N. */
|
||||
middle = NN % 2 + N % 2 /* " " middle " the grid. */
|
||||
if N<2 then do; say oops 'tic─tac─toe grid is too small: ' N; exit 13; end
|
||||
pad=left('', sw % NN) /*display padding: 6x6 in 80 columns.*/
|
||||
if hm=='' then hm= "X"; /*define the marker for a human. */
|
||||
if cm=='' then cm= "O" /* " " " " the computer. */
|
||||
hm= aChar(hm, 'human') /*determine if the marker is legitimate*/
|
||||
cm= aChar(cm, 'computer') /* " " " " " " */
|
||||
parse upper value hm cm with uh uc /*use uppercase values is markers: X x*/
|
||||
if uh==uc then cm=word('O X', 1 + (uh=="O") ) /*The human wants Hal's marker? Swap. */
|
||||
if oN<0 then call Hmove middle /*Hal moves first? Then choose middling*/
|
||||
else call showGrid /*showGrid also checks for wins & draws*/
|
||||
do forever /*'til the cows come home (or QUIT). */
|
||||
call CBLF /*process carbon-based lifeform's move.*/
|
||||
call Hal /*determine Hal's (the computer) move.*/
|
||||
end /*forever*/ /*showGrid subroutine does wins & draws*/
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
ab: parse arg bx; if bx\==' ' then return bx /*test if the marker isn't a blank.*/
|
||||
say oops 'character code for' whoseX "marker can't be a blank."
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
if oN<0 then call Hmove middle /*Hal moves first? Then choose middling*/
|
||||
else call showGrid /*showGrid also checks for wins & draws*/
|
||||
|
||||
/*tic─tac─toe game───►*/ do forever /*'til the cows come home (or QUIT). */
|
||||
/*tic─tac─toe game───►*/ call CBLF /*process carbon─based lifeform's move.*/
|
||||
/*tic─tac─toe game───►*/ call Hal /*determine Hal's (the computer) move.*/
|
||||
/*tic─tac─toe game───►*/ end /*forever*/ /*showGrid subroutine does wins & draws*/
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
aChar: parse arg x,whoseX; L=length(x) /*process markers.*/
|
||||
if L==1 then return ab(x) /*1 char, as is. */
|
||||
if L==2 & datatype(x,'X') then return ab(x2c(x)) /*2 chars, hex. */
|
||||
if L==3 & datatype(x,'W') then return ab(d2c(x)) /*3 chars, decimal*/
|
||||
say oops 'illegal character or character code for' whoseX "marker: " x
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
if L==1 then return testB( x ) /*1 char, as is. */
|
||||
if L==2 & datatype(x, 'X') then return testB( x2c(x) ) /*2 chars, hex. */
|
||||
if L==3 & datatype(x, 'W') & , /*3 chars, decimal*/
|
||||
x>=0 & x<256 then return testB( d2c(x) ) /*···and in range.*/
|
||||
say oops 'illegal character or character code for' whoseX "marker: " x
|
||||
exit 13 /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
CBLF : prompt='Please enter a' cell@ "to place your next marker ["hm'] (or Quit):'
|
||||
do forever; say $ prompt; parse pull x 1 ux 1 ox; upper ux
|
||||
if datatype(ox,'W') then ox=ox/1 /*maybe normalize cell number: +0007. */
|
||||
select
|
||||
when abbrev('QUIT',ux,1) then call tell 'quitting.'
|
||||
when x='' then iterate /*Nada? Try again.*/
|
||||
when words(x)\==1 then say oops "too many" cell# 'specified:' x
|
||||
when \datatype(x,'N') then say oops cell@ "isn't numeric: " x
|
||||
when \datatype(x,'W') then say oops cell@ "isn't an integer: " x
|
||||
when x=0 then say oops cell@ "can't be zero: " x
|
||||
when x<0 then say oops cell@ "can't be negative: " x
|
||||
when x>NN then say oops cell@ "can't exceed " NN
|
||||
when @.ox\=='' then say oops cell@ "is already occupied: " x
|
||||
otherwise leave /*forever*/
|
||||
CBLF: prompt='Please enter a cell number to place your next marker ['hm"] (or Quit):"
|
||||
|
||||
do forever; say $ prompt
|
||||
parse pull x 1 ux 1 ox; upper ux /*get versions of answer; uppercase ux*/
|
||||
if datatype(ox, 'W') then ox=ox / 1 /*normalize cell number: +0007 ───► 7 */
|
||||
/*(division by unity normalizes a num.)*/
|
||||
select /*perform some validations of X (cell#)*/
|
||||
when abbrev('QUIT',ux,1) then call tell 'quitting.'
|
||||
when x='' then iterate /*Nada? Try again.*/
|
||||
when words(x)\==1 then say oops "too many" cell# 'specified:' x
|
||||
when \datatype(x, 'N') then say oops "cell number isn't numeric: " x
|
||||
when \datatype(x, 'W') then say oops "cell number isn't an integer: " x
|
||||
when x=0 then say oops "cell number can't be zero: " x
|
||||
when x<0 then say oops "cell number can't be negative: " x
|
||||
when x>NN then say oops "cell number can't exceed " NN
|
||||
when @.ox\=='' then say oops "cell number is already occupied: " x
|
||||
otherwise leave /*forever*/
|
||||
end /*select*/
|
||||
|
||||
end /*forever*/
|
||||
/* [↓] OX is a normalized version of X*/
|
||||
@.ox=hm /*place a marker for the human (CLBF). */
|
||||
call showGrid /*and display the tic─tac─toe grid. */
|
||||
return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
Hal: select /*Hal tries various moves. */
|
||||
when win(cm,N-1) then call Hmove , ec /*is this the winning move?*/
|
||||
when win(hm,N-1) then call Hmove , ec /* " " a blocking " */
|
||||
when @.middle=='' then call Hmove middle /*pick the center move. */
|
||||
when @.N.N=='' then call Hmove , N N /*bottom right corner move.*/
|
||||
when @.N.1=='' then call Hmove , N 1 /* " left " " */
|
||||
when @.1.N=='' then call Hmove , 1 N /* top right " " */
|
||||
when @.1.1=='' then call Hmove , 1 1 /* " left " " */
|
||||
otherwise call Hmove , ac /*pick a blank cell " */
|
||||
when win(cm, N-1) then call Hmove , ec /*is this the winning move?*/
|
||||
when win(hm, N-1) then call Hmove , ec /* " " a blocking " */
|
||||
when @.middle== '' then call Hmove middle /*pick the center cell. */
|
||||
when @.N.N == '' then call Hmove , N N /*bottom right corner cell.*/
|
||||
when @.N.1 == '' then call Hmove , N 1 /* " left " " */
|
||||
when @.1.N == '' then call Hmove , 1 N /* top right " " */
|
||||
when @.1.1 == '' then call Hmove , 1 1 /* " left " " */
|
||||
otherwise call Hmove , ac /*pick a blank cell in grid*/
|
||||
end /*select*/
|
||||
return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
Hmove: parse arg Hplace,dr dc; if Hplace=='' then Hplace = (dr-1)*N + dc
|
||||
Hmove: parse arg Hplace,dr dc; if Hplace=='' then Hplace = (dr - 1)*N + dc
|
||||
@.Hplace=cm /*place computer's marker. */
|
||||
say; say $ 'computer places a marker ['cm"] at" cell@ ' ' Hplace
|
||||
say; say $ 'computer places a marker ['cm"] at cell number " Hplace
|
||||
call showGrid
|
||||
return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
showGrid: _=0; open=0; cW=5; cH=3 /*cell width, cell height.*/
|
||||
do r=1 for N; do c=1 for N; _=_+1; @.r.c=@._; open=open|@._==''; end
|
||||
end /*r*/
|
||||
showGrid: _=0; cW=5; cH=3; open=0 /*cell width, cell height.*/
|
||||
do r=1 for N /*construct array of cells.*/
|
||||
do c=1 for N; _=_ + 1; @.r.c=@._; open=open | @._==''
|
||||
end /*c*/
|
||||
end /*r*/ /* [↑] OPEN≡a cell is open*/
|
||||
say /* [↑] create grid coörds.*/
|
||||
z=0; do j=1 for N /* [↓] show grids&markers.*/
|
||||
do t=1 for cH; _=; __= /*MK is a marker in a cell.*/
|
||||
do k=1 for N; if t==2 then z=z+1; mk=; c#=
|
||||
if t==2 then do; mk=@.z; c#=z; end /*c# is cell number*/
|
||||
_= _||jam||center(mk,cW); __= __ || jam || center(c#, cW)
|
||||
do k=1 for N; if t==2 then z=z + 1; mk=; c#=
|
||||
if t==2 then do; mk=@.z; c#=z /*c# is cell number*/
|
||||
end
|
||||
_= _ || jam || center(mk, cW)
|
||||
__= __ || jam || center(c#, cW)
|
||||
end /*k*/
|
||||
say pad substr(_,2) pad translate(substr(__,2), sing,dbl)
|
||||
end /*t*/
|
||||
if j==N then leave; _=
|
||||
do b=1 for N; _=_ || junc || copies(bar, cW); end /*b*/
|
||||
say pad substr(_,2) pad translate(substr(_,2), sing, dbl)
|
||||
say pad substr(_, 2) pad translate( substr(__, 2), single, dbl)
|
||||
end /*t*/ /* [↑] show a line*/
|
||||
if j==N then leave
|
||||
_=
|
||||
do b=1 for N; _=_ || junc || copies(bar, cW)
|
||||
end /*b*/ /* [↑] a grid part*/
|
||||
say pad substr(_, 2) pad translate( substr(_, 2), single, dbl)
|
||||
end /*j*/
|
||||
say
|
||||
if win(hm) then call tell 'You ('hm") won!"
|
||||
if win(hm) then call tell 'You ('hm") won"copies('!',random(1, 5) )
|
||||
if win(cm) then call tell 'The computer ('cm") won."
|
||||
if \open then call tell 'This tic─tac─toe game is a draw.'
|
||||
if \open then call tell 'This tic─tac─toe game is a draw (a cat scratch).'
|
||||
return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
tell: do 4; say; end; say center(' 'arg(1)" ", sw, '─'); do 5; say; end; exit
|
||||
tell: do 4; say; end; say center(' 'arg(1)" ", sw, '─'); do 5; say; end; exit
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
win: parse arg wm,w; if w=='' then w=N /*see if there are W of markers*/
|
||||
ac=; do r=1 for N; _=0; ec= /*see if any rows are a winner*/
|
||||
do c=1 for N; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c; end
|
||||
if ec\=='' then ac=ec; if _==N | (_>=w & ec\=='') then return 1
|
||||
end /*r*/ /*w=N-1? Checking for near win*/
|
||||
do c=1 for N; _=0; ec= /*see if any cols are a winner*/
|
||||
do r=1 for N; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c; end
|
||||
if ec\=='' then ac=ec; if _==N | (_>=w & ec\=='') then return 1
|
||||
end /*r*/ /*EC is a R,C version of cell #*/
|
||||
_=0; ec= /*A winning descending diag. ? */
|
||||
do d=1 for N; _=_+ (@.d.d==wm); if @.d.d=='' then ec=d d; end
|
||||
if _==N | (_>=w & ec\=='') then return 1
|
||||
_=0; ec=; r=0 /*A winning ascending diagonal?*/
|
||||
do c=N for N by -1; r=r+1; _=_+ (@.r.c==wm); if @.r.c=='' then ec=r c
|
||||
end /*r*/
|
||||
if _==N | (_>=w & ec\=='') then return 1
|
||||
return 0
|
||||
testB: parse arg bx; if bx\==' ' then return bx /*test if the marker isn't a blank.*/
|
||||
say oops 'character code for' whoseX "marker can't be a blank."
|
||||
exit 13 /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
win: parse arg wm,w; if w=='' then w=N /* [↓] see if there is a win. */
|
||||
ac= /* [↓] EC ≡ means Empty Cell. */
|
||||
do r=1 for N; _=0; ec= /*see if any rows are a winner*/
|
||||
do c=1 for N; _=_ + (@.r.c==wm) /*count the # of markers in col*/
|
||||
if @.r.c=='' then ec=r c /*Cell empty? Then remember it*/
|
||||
end /*c*/ /* [↓] AX≡means avaiable cell.*/
|
||||
if ec\=='' then ac=ec /*Found an empty? Then use it.*/
|
||||
if _==N | (_>=w & ec\=='') then return 1=1 /*a winner has been determined.*/
|
||||
end /*r*/ /*w=N-1? Checking for near win*/
|
||||
|
||||
do c=1 for N; _=0; ec= /*see if any cols are a winner*/
|
||||
do r=1 for N; _=_ + (@.r.c==wm) /*count the # of markers in row*/
|
||||
if @.r.c=='' then ec=r c /*Cell empty? Then remember it*/
|
||||
end /*r*/
|
||||
if ec\=='' then ac=ec /*Found an empty? Then remember*/
|
||||
if _==N | (_>=w & ec\=='') then return 1=1 /*a winner has been determined.*/
|
||||
end /*c*/
|
||||
_=0
|
||||
ec= /*EC≡location of an empty cell.*/
|
||||
do d=1 for N; _=_ + (@.d.d==wm) /*A winning descending diag. ? */
|
||||
if @.d.d=='' then ec=d d /*Empty cell? Then note cell #*/
|
||||
end /*d*/
|
||||
|
||||
if _==N | (_>=w & ec\=='') then return 1=1 /*a winner has been determined.*/
|
||||
_=0; r=1
|
||||
do c=N for N by -1; _=_ + (@.r.c==wm) /*A winning ascending diagonal?*/
|
||||
if @.r.c=='' then ec=r c /*Empty cell? Then note cell #*/
|
||||
r=r + 1 /*bump the counter for the rows*/
|
||||
end /*c*/
|
||||
|
||||
if _==N | (_>=w & ec\=='') then return 1=1 /*a winner has been determined.*/
|
||||
return 0 /*no winner " " " */
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class Board(aBoard : List[Char] = BaseBoard) {
|
|||
}
|
||||
|
||||
|
||||
object TicTacToe extends Application {
|
||||
object TicTacToe extends App {
|
||||
|
||||
def play(board : Board, turn : Char) {
|
||||
|
||||
|
|
|
|||
126
Task/Tic-tac-toe/VBA/tic-tac-toe.vba
Normal file
126
Task/Tic-tac-toe/VBA/tic-tac-toe.vba
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
Option Explicit
|
||||
|
||||
Private Lines(1 To 3, 1 To 3) As String
|
||||
Private Nb As Byte, player As Byte
|
||||
Private GameWin As Boolean, GameOver As Boolean
|
||||
|
||||
Sub Main_TicTacToe()
|
||||
Dim p As String
|
||||
|
||||
InitLines
|
||||
printLines Nb
|
||||
Do
|
||||
p = WhoPlay
|
||||
Debug.Print p & " play"
|
||||
If p = "Human" Then
|
||||
Call HumanPlay
|
||||
GameWin = IsWinner("X")
|
||||
Else
|
||||
Call ComputerPlay
|
||||
GameWin = IsWinner("O")
|
||||
End If
|
||||
If Not GameWin Then GameOver = IsEnd
|
||||
Loop Until GameWin Or GameOver
|
||||
If Not GameOver Then
|
||||
Debug.Print p & " Win !"
|
||||
Else
|
||||
Debug.Print "Game Over!"
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Sub InitLines(Optional S As String)
|
||||
Dim i As Byte, j As Byte
|
||||
Nb = 0: player = 0
|
||||
For i = LBound(Lines, 1) To UBound(Lines, 1)
|
||||
For j = LBound(Lines, 2) To UBound(Lines, 2)
|
||||
Lines(i, j) = "#"
|
||||
Next j
|
||||
Next i
|
||||
End Sub
|
||||
|
||||
Sub printLines(Nb As Byte)
|
||||
Dim i As Byte, j As Byte, strT As String
|
||||
Debug.Print "Loop " & Nb
|
||||
For i = LBound(Lines, 1) To UBound(Lines, 1)
|
||||
For j = LBound(Lines, 2) To UBound(Lines, 2)
|
||||
strT = strT & Lines(i, j)
|
||||
Next j
|
||||
Debug.Print strT
|
||||
strT = vbNullString
|
||||
Next i
|
||||
End Sub
|
||||
|
||||
Function WhoPlay(Optional S As String) As String
|
||||
If player = 0 Then
|
||||
player = 1
|
||||
WhoPlay = "Human"
|
||||
Else
|
||||
player = 0
|
||||
WhoPlay = "Computer"
|
||||
End If
|
||||
End Function
|
||||
|
||||
Sub HumanPlay(Optional S As String)
|
||||
Dim L As Byte, C As Byte, GoodPlay As Boolean
|
||||
|
||||
Do
|
||||
L = Application.InputBox("Choose the row", "Numeric only", Type:=1)
|
||||
If L > 0 And L < 4 Then
|
||||
C = Application.InputBox("Choose the column", "Numeric only", Type:=1)
|
||||
If C > 0 And C < 4 Then
|
||||
If Lines(L, C) = "#" And Not Lines(L, C) = "X" And Not Lines(L, C) = "O" Then
|
||||
Lines(L, C) = "X"
|
||||
Nb = Nb + 1
|
||||
printLines Nb
|
||||
GoodPlay = True
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Loop Until GoodPlay
|
||||
End Sub
|
||||
|
||||
Sub ComputerPlay(Optional S As String)
|
||||
Dim L As Byte, C As Byte, GoodPlay As Boolean
|
||||
|
||||
Randomize Timer
|
||||
Do
|
||||
L = Int((Rnd * 3) + 1)
|
||||
C = Int((Rnd * 3) + 1)
|
||||
If Lines(L, C) = "#" And Not Lines(L, C) = "X" And Not Lines(L, C) = "O" Then
|
||||
Lines(L, C) = "O"
|
||||
Nb = Nb + 1
|
||||
printLines Nb
|
||||
GoodPlay = True
|
||||
End If
|
||||
Loop Until GoodPlay
|
||||
End Sub
|
||||
|
||||
Function IsWinner(S As String) As Boolean
|
||||
Dim i As Byte, j As Byte, Ch As String, strTL As String, strTC As String
|
||||
|
||||
Ch = String(UBound(Lines, 1), S)
|
||||
'check lines & columns
|
||||
For i = LBound(Lines, 1) To UBound(Lines, 1)
|
||||
For j = LBound(Lines, 2) To UBound(Lines, 2)
|
||||
strTL = strTL & Lines(i, j)
|
||||
strTC = strTC & Lines(j, i)
|
||||
Next j
|
||||
If strTL = Ch Or strTC = Ch Then IsWinner = True: Exit For
|
||||
strTL = vbNullString: strTC = vbNullString
|
||||
Next i
|
||||
'check diagonales
|
||||
strTL = Lines(1, 1) & Lines(2, 2) & Lines(3, 3)
|
||||
strTC = Lines(1, 3) & Lines(2, 2) & Lines(3, 1)
|
||||
If strTL = Ch Or strTC = Ch Then IsWinner = True
|
||||
End Function
|
||||
|
||||
Function IsEnd() As Boolean
|
||||
Dim i As Byte, j As Byte
|
||||
|
||||
For i = LBound(Lines, 1) To UBound(Lines, 1)
|
||||
For j = LBound(Lines, 2) To UBound(Lines, 2)
|
||||
If Lines(i, j) = "#" Then Exit Function
|
||||
Next j
|
||||
Next i
|
||||
IsEnd = True
|
||||
End Function
|
||||
Loading…
Add table
Add a link
Reference in a new issue