Skip to content

Instantly share code, notes, and snippets.

@akarelas
Last active November 18, 2017 08:34
Show Gist options
  • Save akarelas/c5ca56eef277dafe74dc3bbafe197df2 to your computer and use it in GitHub Desktop.
Save akarelas/c5ca56eef277dafe74dc3bbafe197df2 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe in Perl - Play against the computer and never win
#!/usr/bin/env perl
use v5.20;
use warnings;
use lib 'local/lib/perl5';
use List::Util qw/ max all /;
use List::MoreUtils 'indexes';
use experimental 'signatures';
my @board = (0) x 9;
my @winning_rows = (
[0..2], [3..5], [6..8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6],
);
sub eqq ($x, $y) {
if (! defined $x) {
return ! defined $y;
}
defined $y or return !!0;
ref($x) eq ref($y) or return !!0;
return $x eq $y;
}
sub status (@board) {
foreach my $row (@winning_rows) {
my @vals = map $board[$_], @$row;
if ($vals[2] == $vals[0] and $vals[1] == $vals[0] and $vals[0]) {
return $vals[0];
}
}
return 0;
}
sub display (@board) {
for (my $i = 0; $i < 9; $i++) {
my $num = $board[$i];
print $num || '_';
print ' ';
if ($i % 3 == 2) { print "\n"; }
}
say '';
}
sub best_move ($player, $is_original, @board) {
my $status = status(@board);
if ($status) {
return (undef, ($status == $player) ? 1 : -1);
}
if (all {$_} @board) { return (undef, 0); }
my @indexes = indexes {! $_} @board;
my @payouts = (-2) x 9;
foreach my $index (@indexes) {
my @new_board = @board;
$new_board[$index] = $player;
my $other_player = 3 - $player;
my ($other_best_move, $other_payout) = best_move($other_player, 0, @new_board);
$payouts[$index] = -$other_payout;
if (! $is_original and $payouts[$index] == 1) { last; }
}
my $max_payout = max(@payouts);
my @possible_moves = indexes {$_ == $max_payout} @payouts;
my $move = $possible_moves[$is_original ? (int rand @possible_moves) : 0];
return wantarray ? ($move, $max_payout) : $move;
}
print "Play first? (y/n) ";
my $who_plays = 2 - !!(scalar(<STDIN>) =~ /\Ay/i);
until (status(@board) or all {$_} @board) {
display(@board);
my $index;
if ($who_plays == 1) {
INPUT: while (1) {
print "Play where? row,column eg 3,2 : ";
my $ans = <STDIN>;
my ($y, $x) = $ans =~ /(\d)/g;
all { /[123]/ } ($x, $y) or next INPUT;
$index = ($y - 1) * 3 + ($x - 1);
eqq($board[$index], 0) or next INPUT;
say '';
last;
} continue {
say "Wrong input, try again";
}
}
else {
$index = best_move(2, 1, @board);
}
$board[$index] = $who_plays;
$who_plays = 3 - $who_plays;
}
my $who_won = status(@board);
if ($who_won == 1) {
say 'You won!';
} elsif ($who_won == 2) {
say 'I won!';
} else {
say 'Tie!';
}
display(@board);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment