Hexapawn: Difference between revisions

11,796 bytes added ,  5 months ago
m
(Hexapawn en Python)
m (→‎{{header|Wren}}: Minor tidy)
 
(3 intermediate revisions by 3 users not shown)
Line 21:
However, unlike the paper this does ''not'' handle mirror games as the same.
I.e. this implementation will need to learn how to respond to white's opening "3 6" move independently from the mirror white opening move of "1 4".
<langsyntaxhighlight lang="go">package main
 
import (
Line 269:
}
return black
}</langsyntaxhighlight>
{{out|Sample Output}}
<pre>
Line 301:
=={{header|JavaScript}}==
You can try it [http://paulo-jorente.de/tests/hexapawn/ here].
<langsyntaxhighlight lang="javascript">
var board, playBtn, turn, memory = [], lastMove = {brd: "", mvi: 0},
clicks = {first: null, second: null}, win = {c: 0, p: 0}, score;
Line 504:
restart();
}
</syntaxhighlight>
</lang>
HTML (testing)
<pre><!DOCTYPE html>
Line 528:
Graphical versions, using Gtk. Unicode has chess symbols! See https://www.compart.com/en/unicode/block/U+2600.
===Learning Version===
<langsyntaxhighlight lang="julia">using Gtk, Base
 
const whitepawn = UInt8('w')
Line 747:
 
hexapawnapp()
</syntaxhighlight>
</lang>
===Pretaught Version===
Unlike full chess, the play here is simple enough to specify perfectly played games with just a 17 move table and one-move lookahead.
<langsyntaxhighlight lang="julia">using Gtk, Base
 
const whitepawn = UInt8('w')
Line 972:
 
hexapawnapp()
</syntaxhighlight>
</lang>
 
=={{header|Pascal}}==
Line 985:
One reason for writing this programme was to support an essay I had planned on the difference between thinking and computation. For instance, for some board shapes it is apparent (via thinking) that the forced winner is known, even obvious, without playing any game. But, via computation, the programme attains this only via slogging through the computation.
===Source===
<syntaxhighlight lang="pascal">
<lang Pascal>
{$N- No serious use of floating-point stuff.}
{$B- Early and safe resolution of If x <> 0 and 1/x...}
Line 3,727:
end;
END.
</syntaxhighlight>
</lang>
===Some results===
It is called Pawnplex because it is not for just a three by three board. Invoke from the command line with <code>Pawnplex.exe ?</code> to obtain a description or <code>Pawnplex.exe 3</code> to run for the original 3×3 problem. A windows interface may allow the specification of parameters to a run (say via a "shortcut") but otherwise, ... open a DOS window... As for one player or the other having a winning strategy, some partial results are shown in the following:
Line 3,762:
</pre>
Which is an array of results, one for each size board - a board having only one row is impossible as the pieces can't be placed. Notice that row 3, column 3 has X, meaning that X has an unbeatable strategy, no matter what O tries, and so on for the other worked-through board sizes. The number of possible games increases very rapidly as the board size increases...
 
=={{header|Perl}}==
Plays in a Tk GUI. Has both trainer capability and variable size boards.
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # https://rosettacode.org/wiki/Hexapawn
use warnings;
use List::AllUtils qw( max each_array reduce shuffle );
use Tk;
 
my @argv = @ARGV;
my $size = max 3, shift // 3;
my $train = shift // 1e3;
my $forward = qr/.{$size}/s;
my $take = qr/.{@{[$size - 1]}}(?:..)?/s;
my $message = 'Click on Pawn';
my (@played, %scores, $from, $active);
my $board = my $start = "b\n-\nw\n" =~
s/-\n/$& x ($size - 2)/er =~ s/./$& x $size/ger;
 
my $mw = MainWindow->new;
$mw->geometry( '+600+300' );
$mw->title( 'RosettaCode Hexapawn' );
my $grid = $mw->Frame->pack;
my @squares = map {
my $row = $_;
map {
my $col = $_;
my $g = $grid->Canvas( -width => 100, -height => 100,
-bd => 0, -relief => 'flat', -highlightthickness => 0,
-bg => ($row+$col) % 2 ? 'gray80' : 'gray60',
)->grid( -row => $row, -column => $col, -sticky => 'nsew' );
$g->Tk::bind('<1>' => sub{ click( $col, $row ) } );
$g->Tk::bind("<ButtonRelease-$_>" => sub{$g->yviewMoveto(0)} ) for 4, 5;
$g } 0 .. $size - 1
} 0 .. $size - 1;
my $label = $mw->Label( -textvariable => \$message,
)->pack( -side => 'bottom', -expand => 1, -fill => 'both' );
$mw->Button(-text => 'Exit', -command => sub {$mw->destroy},
)->pack( -side => 'right', -fill => 'x', -expand => 0 );
$mw->Button(-text => 'New Game', -command => \&newgame,
)->pack( -side => 'right', -fill => 'x', -expand => 1 );
$mw->Button(-text => 'Train', -command => \&train,
)->pack( -side => 'right', -fill => 'x', -expand => 0 );
newgame();
MainLoop;
-M $0 < 0 and exec "$0 @argv";
 
sub findwhite
{
my @moves;
$board =~ /(?:-($forward)w|b($take)w)(?{ push @moves, "$`w$+-$'"; })(*FAIL)/;
@moves;
}
 
sub findblack
{
my @moves;
$board =~ /(?:b($forward)-|b($take)w)(?{ push @moves, "$`-$+b$'"; })(*FAIL)/;
@moves;
}
 
sub newgame
{
$board = $start;
@played = ();
$from = undef;
$active = 1;
$message = 'Click on Pawn';
$label->configure( -bg => 'gray85' );
show();
}
 
sub train
{
$message = 'Training';
$label->configure( -bg => 'yellow' );
$mw->update;
for ( 1 .. $train )
{
$board = $start;
my @whitemoves = findwhite;
my @blackmoves;
@played = ();
while( 1 )
{
$board = $whitemoves[rand @whitemoves];;
if( $board =~ /^.*w/ or not (@blackmoves = findblack) )
{
$scores{$_}++ for map {$_, s/.+/ reverse $& /ger } @played;
last;
}
push @played, $board = $blackmoves[rand @blackmoves];
if( $board =~ /b.*$/ or not (@whitemoves = findwhite) )
{
$scores{$_}-- for map {$_, s/.+/ reverse $& /ger } @played;
last;
}
}
}
print "score count: @{[ scalar keys %scores ]}\n";
newgame();
}
 
sub scale { map 100 * $_ >> 3, @_ };
 
sub show
{
my $ea = each_array(@{[ $board =~ /./g ]}, @squares );
while( my ($piece, $canvas) = $ea->() )
{
$canvas->delete('all');
$piece eq '-' and next;
$canvas->createOval(scale(3, 3, 5, 5));
$canvas->createArc(scale(2, 4.8, 6, 9), -start => 0, -extent => 180);
$canvas->itemconfigure('all', -outline => undef,
-fill => $piece eq 'w' ? 'white' : 'black');
}
}
 
sub click
{
my ($col, $row) = @_;
$active or return;
my $pos = $row * ($size + 1) + $col;
if( 'w' eq substr $board, $pos, 1 )
{
$from = $pos;
$message = 'Click on Destination';
}
elsif( defined $from )
{
my $new = $board;
substr $new, $from, 1, '-';
substr $new, $pos, 1, 'w';
if( grep $_ eq $new, findwhite )
{
$board = $new;
my @blackmoves = findblack;
if( $board =~ /^.*w/ or @blackmoves == 0 )
{
$active = 0;
$message = 'White Wins';
$label->configure( -bg => 'green' );
$scores{$_}++ for map {$_, s/.+/ reverse $& /ger } @played;
}
else
{
$from = undef;
$message = 'Blacks Move';
push @played, $board = reduce
{ ($scores{$a} // 0) < ($scores{$b} // 0) ? $a : $b }
shuffle @blackmoves;
if( $board =~ /b.*$/ or not findwhite )
{
$active = 0;
$message = 'Black Wins';
$label->configure( -bg => 'red' );
$scores{$_}-- for map {$_, s/.+/ reverse $& /ger } @played;
}
else
{
$message = 'Click on Pawn';
}
}
show;
}
else
{
$mw->bell;
$message = 'Invalid move';
$mw->after( 500 => sub { $message = 'Click on Destination' } );
}
}
}</syntaxhighlight>
 
=={{header|Phix}}==
Line 3,770 ⟶ 3,945:
Displays full training data (for black and white), and last game history.
Mouse-operated: Hover on an appropriate sector of one of your pieces until an arrow shows, and click.
<langsyntaxhighlight Phixlang="phix">-- demo\rosetta\hexapawn.exw
include pGUI.e
 
Line 4,278 ⟶ 4,453:
end procedure
main()</langsyntaxhighlight>
There is also a straightforward translation of Go in demo\rosetta\heaxpawn_go.exw if anyone is interested...
 
Line 4,284 ⟶ 4,459:
El código es de Matt Hughes - https://github.com/mch/hexapawn
Yo solo lo transcribo.
<langsyntaxhighlight lang="python">
#!/usr/bin/env python3
import sys
Line 4,450 ⟶ 4,625:
 
play_game(get_human_move, get_human_move)
</syntaxhighlight>
</lang>
 
=={{header|Wren}}==
{{trans|Python}}
{{libheader|Wren-ioutil}}
{{libheader|Wren-str}}
Tweaked a little - allows input in either lower or upper case and always draws final board position.
<syntaxhighlight lang="wren">import "./ioutil" for Input, Output
import "./str" for Str
 
var blackPawn = " \u265f "
var whitePawn = " \u2659 "
var emptySquare = " "
 
var drawBoard = Fn.new { |boardData|
var bgBlack = "\e[48;5;237m"
var bgWhite = "\e[48;5;245m"
var clearToEol = "\e[0m\e[K\n"
var board = [
"1 ", bgBlack, boardData[0][0], bgWhite, boardData[0][1], bgBlack, boardData[0][2], clearToEol,
"2 ", bgWhite, boardData[1][0], bgBlack, boardData[1][1], bgWhite, boardData[1][2], clearToEol,
"3 ", bgBlack, boardData[2][0], bgWhite, boardData[2][1], bgBlack, boardData[2][2], clearToEol,
" A B C\n"
]
System.print()
Output.fwrite(board.join())
}
 
var getMovementDirection = Fn.new { |color|
var direction = -1
if (color == blackPawn) {
direction = 1
} else if (color != whitePawn) {
Fiber.abort("Invalid piece color")
}
return direction
}
 
var getOtherColor = Fn.new { |color|
if (color == blackPawn) {
return whitePawn
} else if (color == whitePawn) {
return blackPawn
} else {
Fiber.abort("Invalid piece color")
}
}
 
var getAllowedMoves = Fn.new { |boardData, row, col|
var allowedMoves = []
if (boardData[row][col] == emptySquare) return allowedMoves
var color = boardData[row][col]
var otherColor = getOtherColor.call(color)
var direction = getMovementDirection.call(color)
if (row + direction < 0 || row + direction > 2) return allowedMoves
if (boardData[row + direction][col] == emptySquare) allowedMoves.add("f")
if (col > 0 && boardData[row + direction][col - 1] == otherColor) allowedMoves.add("dl")
if (col < 2 && boardData[row + direction][col + 1] == otherColor) allowedMoves.add("dr")
return allowedMoves
}
 
var getHumanMove = Fn.new { |boardData, color|
// The direction the pawns may move depends on the colour; assuming that white starts at the bottom.
var direction = getMovementDirection.call(color)
var validInputs = {
"a1": [0,0], "b1": [0,1], "c1": [0,2],
"a2": [1,0], "b2": [1,1], "c2": [1,2],
"a3": [2,0], "b3": [2,1], "c3": [2,2]
}
var keys = validInputs.keys.toList
while (true) {
var piecePosn = Str.lower(Input.text("What %(color) do you want to move? : ", 2, 2))
if (!keys.contains(piecePosn)) {
System.print("LOL that's not a valid position! Try again.")
continue
}
var rc = validInputs[piecePosn]
var row = rc[0]
var col = rc[1]
var piece = boardData[row][col]
if (piece == emptySquare) {
System.print("What are you trying to pull, there's no piece in that space!")
continue
}
if (piece != color) {
System.print("LOL that's not your piece, try again!")
continue
}
var allowedMoves = getAllowedMoves.call(boardData, row, col)
 
if (allowedMoves.count == 0) {
System.print("LOL nice try. That piece has no valid moves.")
continue
}
var move = allowedMoves.toList[0]
if (allowedMoves.count > 1) {
var options = allowedMoves.join(", ")
move = Str.lower(Input.text("What move do you want to make [%(options)]? : ", 1, 2))
if (!allowedMoves.contains(move)) {
System.print("LOL that move is not allowed. Try again.")
continue
}
}
if (move == "f") {
boardData[row + direction][col] = boardData[row][col]
} else if (move == "dl") {
boardData[row + direction][col - 1] = boardData[row][col]
} else if (move == "dr") {
boardData[row + direction][col + 1] = boardData[row][col]
}
boardData[row][col] = emptySquare
return boardData
}
}
 
var isGameOver = Fn.new { |boardData|
if (boardData[0][0] == whitePawn || boardData[0][1] == whitePawn || boardData[0][2] == whitePawn) {
return whitePawn
}
if (boardData[2][0] == blackPawn || boardData[2][1] == blackPawn || boardData[2][2] == blackPawn) {
return blackPawn
}
var whiteCount = 0
var blackCount = 0
var blackAllowedMoves = []
var whiteAllowedMoves = []
for (i in 0..2) {
for (j in 0..2) {
var moves = getAllowedMoves.call(boardData, i, j)
if (boardData[i][j] == whitePawn) {
whiteCount = whiteCount + 1
if (moves.count > 0) whiteAllowedMoves.add([i, j, moves])
} else if (boardData[i][j] == blackPawn) {
blackCount = blackCount + 1
if (moves.count > 0) blackAllowedMoves.add([i, j, moves])
}
}
}
if (whiteCount == 0 || whiteAllowedMoves.count == 0) return blackPawn
if (blackCount == 0 || blackAllowedMoves.count == 0) return whitePawn
return "LOL NOPE"
}
 
var playGame = Fn.new { |blackMove, whiteMove|
var boardData = [
[blackPawn, blackPawn, blackPawn],
[emptySquare, emptySquare, emptySquare],
[whitePawn, whitePawn, whitePawn]
]
var lastPlayer = blackPawn
var nextPlayer = whitePawn
while (isGameOver.call(boardData) == "LOL NOPE") {
drawBoard.call(boardData)
if (nextPlayer == blackPawn) {
boardData = blackMove.call(boardData, nextPlayer)
} else {
boardData = whiteMove.call(boardData, nextPlayer)
}
var temp = lastPlayer
lastPlayer = nextPlayer
nextPlayer = temp
}
drawBoard.call(boardData)
var winner = isGameOver.call(boardData)
System.print("Congratulations %(winner)!")
}
 
playGame.call(getHumanMove, getHumanMove)</syntaxhighlight>
 
{{out}}
Sample game:
<pre>
1 ♟ ♟ ♟
2
3 ♙ ♙ ♙
A B C
What ♙ do you want to move? : b3
 
1 ♟ ♟ ♟
2 ♙
3 ♙ ♙
A B C
What ♟ do you want to move? : a1
What move do you want to make [f, dr]? : dr
 
1 ♟ ♟
2 ♟
3 ♙ ♙
A B C
What ♙ do you want to move? : c3
What move do you want to make [f, dl]? : dl
 
1 ♟ ♟
2 ♙
3 ♙
A B C
What ♟ do you want to move? : c1
What move do you want to make [f, dl]? : f
 
1 ♟
2 ♙ ♟
3 ♙
A B C
What ♙ do you want to move? : a3
 
1 ♟
2 ♙ ♙ ♟
3
A B C
What ♟ do you want to move? : c2
 
1 ♟
2 ♙ ♙
3 ♟
A B C
Congratulations ♟ !
</pre>
9,476

edits