Alak was invented by the mathematician A. K. Dewdney, and described in his 1984 book Planiverse. The rules of Alak are simple:
Alak is a two-player game played on a one-dimensional board with eleven slots on it. Each slot can hold at most one piece at a time. There's two kinds of pieces, "x" and "o". x's belong to one player, o's to the other. The initial configuration of the board is:
xxxx___oooo
The players take turns moving. At each turn, each player can move only one piece, once. A player cannot pass up on his turn. A player can move any one of his pieces to the next unoccupied slot to its right or left, which may involve jumping over occupied slots. A player cannot move a piece off the side of the board.
If a move creates a pattern where the opponent's pieces are surrounded, on both sides, by two pieces of the mover's color (with no intervening unoccupied blank slot), then those surrounded pieces are removed from the board.
The goal of the game is to remove all of your opponent's pieces, at which point the game ends. Removing all-but-one ends the game as well, since the opponent can't surround you with one piece, and so will always lose within a few moves anyway.
I found this game online and was wondering: can it be golfed?
Rules of the golf
- Your code must follow all the rules in the game, handling captures, proper moving, etc. (only exception is you don't have to add a bot, but you must have both players controlled somehow, and one player must be human).
- Input must be move piece at tile X to tile Y, or quit. For example, you can use
1 4to say 'move this piece at tile 1 to tile 4'.quitwould end the program, although using Control-C would be acceptable. You also have to check if a move is invalid (by going outside the board or moving somewhere that you would have to cross over unoccupied spaces to get to or sending a message that is not a pair of tiles orquit). - Outputs for players winning and invalid must be
P1 WINS,P2 WINS, andINVALID, respectively. (All of these are 7 characters.) - Output must show the board. That's all that's required.
- It doesn't matter if you use any aids like numbered tiles or other pieces.
The challenge ends if:
- One answer gets 50 votes
- One answer remains the top-voted for 3 weeks, and no other answers were posted in that time
and the challenge has at least 3 answers (so there's some real competition).
Rules of the game
- The player on the left must start first.
- Only one piece occupies a square at a time. You move the piece left or right until it hits an unoccupied space. The board does not wrap, and you can't move through unoccupied areas. For example:
xoo__o. Here, thexmoving right would change the board to_oox_o.xxooo_. Here, the farthest-leftxcould move to yield_xooox, which captures theos, leaving_x___x.x__oox. Here, theos are not captured (there is still a gap). Capture is not possible because you can't move through unoccupied spaces. Thexon the left could only move one space, because there are no other pieces in between (leaving_x_oox).
- Multiple adjacent pieces can be captured at once if the group is surrounded by the opponent's pieces. E.g. from
x_ooxto_xooxwill capture bothos and result in_x__x. - If after a move, you first capture the opponent's pieces, before checking if your own piece should be remove. Take two examples:
o_oxxtooxox_. First, the secondois captured,ox_x_, so the firstxremains on the board.o_ooxtooxoo_. This time, none of theos are captured, so thexis captured instead.- If you have only one piece, the game ends, because you can't capture with just one piece.
Let the games begin! I look forward to seeing what you come up with.
-
\$\begingroup\$ Comments purged, as they were obsolete. Please notify me of any comments that should be undeleted. \$\endgroup\$Doorknob– Doorknob2015年04月03日 19:26:02 +00:00Commented Apr 3, 2015 at 19:26
5 Answers 5
C, (削除) 617 (削除ここまで) 592 bytes
#define O(x)(x-'x'?'x':'o')
q,f,t,k,i,x=4,o=4,*d;main(){char*A,Q[9],c='x',b[]="xxxx___oooo";printf(b);while(!q){scanf(" %8[^\n]%*[^\n]",Q);if(!strcmp(Q,"quit"))break;f=*Q>47&&*Q<58?atoi(Q):-1;A=f>9?Q+2:Q+1;t=*A==32&&A[1]>47&&A[1]<58?atoi(A+1):-1;i=t==f&&t<0&&f<0?1:0;for(k=f;k!=t;k+=(t-f)/abs(t-f))if(b[k]==95||b[t]-95||b[f]-c)i=1;if(i){printf("INVALID");continue;}b[t]=c;b[f]=95;for(t=0;t<2;t++){d=c-'x'?&o:&x;for(k=1;k<11;k++)if(b[k]==O(c)){for(i=k+1;b[i]==O(c)&&b[i];i++);if(b[i]==c&&b[k-1]==c)while(k<i)b[k++]=95,(*d)--;}c=t?c:O(c);}printf(b);if(o<2||x<2)printf("P%d WINS",(x>1)+1),q=1;}}
Unraveled:
#define O(x)(x-'x'?'x':'o')
q,f,t,k,i,x=4,o=4,*d;
main(){
char*A,Q[9],c='x',b[]="xxxx___oooo";
printf(b);
while(!q){
scanf(" %8[^\n]%*[^\n]",Q);
if(!strcmp(Q,"quit"))break;
f=*Q>47&&*Q<58?atoi(Q):-1;
A=f>9?Q+2:Q+1;
t=*A==32&&A[1]>47&&A[1]<58?atoi(A+1):-1;
i=t==f&&t<0&&f<0?1:0;
for(k=f;k!=t;k+=(t-f)/abs(t-f))
if(b[k]==95||b[t]-95||b[f]-c)
i=1;
if(i){
printf("INVALID");
continue;
}
b[t]=c;
b[f]=95;
for(t=0;t<2;t++){
d=c-'x'?&o:&x;
for(k=1;k<11;k++)
if(b[k]==O(c)){
for(i=k+1;b[i]==O(c)&&b[i];i++);
if(b[i]==c&&b[k-1]==c)
while(k<i)b[k++]=95,(*d)--;
}
c=t?c:O(c);
}
printf(b);
if(o<2||x<2)printf("P%d WINS",(x>1)+1),q=1;
}
}
I really wanted to get this one in ~400 bytes, but there are a lot of little rules here and the input processing ended up pretty obnoxious. I'm definitely not done with this. Here's a set of sample runs that covers just about everything:
xxxx___oooo0 4
_xxxx__oooo7 6
_xxxx_o_ooo4 5
_xxx_xo_ooo8 6
INVALID8 7
_xxx_xoo_oo3 4
_xx_xxoo_oo7 3
_xxo__o__oo1 4
__x_x_o__oo10 9
INVALID10 8
__x_x_o_oo_2 3
___xx_o_oo_6 5
___xxo__oo_6 6
INVALID5 5
INVALID3 6
____x_x_oo_8 7
____x_xo_o_6 8
____x__o_o_P2 WINS
xxxx___oooo0 4
_xxxx__oooo10 6
_xxxx_oooo_1 5
__xxxxoooo_9 1
_o____ooo__P2 WINS
xxxx___oooo0 4
_xxxx__oooo7 6
_xxxx_o_ooo1 5
__xxxxo_ooo10 7
__xxxxoooo_2 10
___xxx____xP1 WINS
xxxx___oooo3 4
xxx_x__ooooquits
INVALIDtestme
INVALID3*4
INVALID3 four
INVALIDthree four
INVALIDthisstringislongerthanmybuffer
INVALID10 0
INVALID4 5
INVALID7 6
xxx_x_o_oooquit
If I've misinterpreted something, please let me know!
-
\$\begingroup\$ I tested it, it works well, and nothing was left out. Good job! \$\endgroup\$ASCIIThenANSI– ASCIIThenANSI2015年04月03日 19:10:28 +00:00Commented Apr 3, 2015 at 19:10
-
\$\begingroup\$ You can save a few bytes by replacing
printf("INVALID");withputs("INVALID");,o<2||x<2witho<2|x<2andprintf(b);while(!q){withfor(printf(b);!q;){\$\endgroup\$es1024– es10242015年05月05日 07:19:30 +00:00Commented May 5, 2015 at 7:19 -
\$\begingroup\$ 494 bytes \$\endgroup\$ceilingcat– ceilingcat2020年09月22日 21:42:03 +00:00Commented Sep 22, 2020 at 21:42
PHP - 505
<?php
$s="xxxx___ooo".$y=o;$x=x;$c=function($m)use(&$x){return$x.str_repeat('_',strlen($m[1])).$x;};$e='$s=preg_replace_callback("~$x($y+)$x~",$c,$s);';$_=substr_count;while(true){echo$s;if($_($s,x)<2)die("P2 WINS");if($_($s,o)<2)die("P1 WINS");$i=trim(fgets(STDIN));if($i=='quit')die;if(!preg_match('!^(\d+) (\d+)$!',$i,$m)||$s[$f=$m[1]]!=$x||$s[$t=$m[2]]!="_"||(0&($a=min($f,$t))&$b=max($f,$t))||$_($s,"_",$a,($b-$a)+1)>1)echo"INVALID\n";else{$s[$f]='_';$s[$t]=$x;eval($e);$z=$x;$x=$y;$y=$z;eval($e);}}
Notices must be suppressed by redirecting STDERR to /dev/null.
With sane whitespace:
<?php
@$s = "xxxx___ooo".($y = o);
@$x = x;
$c = function($m)usea(&$x){
return$x.str_repeat('_',strlen($m[1])).$x;
};
$e = '$s=preg_replace_callback("~$x($y+)$x~",$c,$s);';
@$_ = substr_count;
while (true){
echo $s;
if (@$_($s,x) < 2) die("P2 WINS");
if (@$_($s,o) < 2) die("P1 WINS");
$i = trim(fgets(STDIN));
if($i == 'quit') die;
if( !preg_match('!^(\d+) (\d+)$!',$i,$m)
|| $s[$f = $m[1]] != $x
|| @$s[$t = $m[2]] != "_"
|| (0 & ($a = min($f, $t)) & $b = max($f, $t))
|| $_($s, "_", $a, ($b - $a) + 1) > 1
) echo "INVALID\n";
else {
$s[$f] = '_';
$s[$t] = $x;
eval($e);
$z = $x;
$x = $y;
$y = $z;
eval($e);
}
}
With the test cases of BrainSteel:
xxxx___oooo0 4
_xxxx__oooo7 6
_xxxx_o_ooo4 5
_xxx_xo_ooo8 6
INVALID
_xxx_xo_ooo8 7
_xxx_xoo_oo3 4
_xx_xxoo_oo7 3
_xxo__o__oo1 4
__x_x_o__oo10 9
INVALID
__x_x_o__oo10 8
__x_x_o_oo_2 3
___xx_o_oo_6 5
___xxo__oo_6 6
INVALID
___xxo__oo_5 5
INVALID
___xxo__oo_3 6
____x_x_oo_8 7
____x_xo_o_6 8
____x__o_o_P2 WINS
xxxx___oooo0 4
_xxxx__oooo10 6
_xxxx_oooo_1 5
__xxxxoooo_9 1
_o____ooo__P2 WINS
xxxx___oooo0 4
_xxxx__oooo7 6
_xxxx_o_ooo1 5
__xxxxo_ooo10 7
__xxxxoooo_2 10
___xxx____xP1 WINS
xxxx___oooo3 4
xxx_x__ooooquits
INVALID
xxx_x__ooootestme
INVALID
xxx_x__oooo3*4
INVALID
xxx_x__oooo3 four
INVALID
xxx_x__oooothree four
INVALID
xxx_x__oooothisstringislongerthanmybuffer
INVALID
xxx_x__oooo10 0
INVALID
xxx_x__oooo4 5
INVALID
xxx_x__oooo7 6
xxx_x_o_oooquit
-
\$\begingroup\$ What do you mean by 'notices/warnings'? \$\endgroup\$ASCIIThenANSI– ASCIIThenANSI2015年05月02日 20:27:30 +00:00Commented May 2, 2015 at 20:27
-
\$\begingroup\$ @ASCIIThenANSI Warnings because of unquoted character literals: PHP Notice: Use of undefined constant o - assumed 'o' in /tmp/pcg-48388.php on line 2. One can redirect them to /dev/null. \$\endgroup\$TimWolla– TimWolla2015年05月02日 20:28:33 +00:00Commented May 2, 2015 at 20:28
-
\$\begingroup\$ Does that break the program? \$\endgroup\$ASCIIThenANSI– ASCIIThenANSI2015年05月02日 20:28:57 +00:00Commented May 2, 2015 at 20:28
-
\$\begingroup\$ @ASCIIThenANSI No, it does work fine is they are redirected to
/dev/null. \$\endgroup\$TimWolla– TimWolla2015年05月02日 20:29:24 +00:00Commented May 2, 2015 at 20:29 -
\$\begingroup\$ Then it's OK to have it as long as the program continues to function properly, and they are redirected to
/dev/null. \$\endgroup\$ASCIIThenANSI– ASCIIThenANSI2015年05月02日 21:51:24 +00:00Commented May 2, 2015 at 21:51
Python 2, (削除) 536 (削除ここまで) (削除) 509 (削除ここまで) (削除) 448 (削除ここまで) 441 bytes
Call via a(); moves are to be entered in the form piece,destination (i.e., 1,4); quit with Ctrl-C. If anyone can see more golfing potential, I'm all ears.
b,r,x='_',lambda p:''.join([p[i]for i in x]),range(11)
def a(m='xo'):
t=w=0;p=dict(zip(x,'xxxx___oooo'))
while w<1:
print r(p);y=m[t%2]
try:
s,v=input();1/all([y==p[s],{v}<{r(p).rfind(b,0,s),r(p).find(b,s)},v-s]);p[s],p[v],h,c=b,y,0,{}
for _ in y,m[-~t%2]:
for i in p:exec{_:"h=1;p.update(c)",b:"h,c=0,{}"}.get(p[i],h*"c[i]=b")
w=min(map(r(p).count,m))<2;t+=1
except:print"INVALID"
print"P%d WINS"%-~(r(p).count('o')<2)
SpecBAS - 718 bytes
SpecBAS is an updated version of Sinclair/ZX BASIC that can run outside of an emulator. (Still interpreted).
Have used some of the new features to get the size down as much as I could.
Line 12 sets up a regex to search for "sandwiched" pieces using inline IF and line 18 uses the wrap around nature of INC (rather than saying INC p: IF p=3 THEN LET p=1)
1 LET b$="xxxx---oooo": LET p=1: LET c$="xo": DIM s=4,4
2 LET v=0: PRINT b$'"[";p;"] ";
3 INPUT m$: IF m$(1)="Q" THEN PRINT "QUIT": STOP
4 LET f=VAL(ITEM$(m,1,ドル" ")): LET t=VAL(ITEM$(m,2,ドル" ")): PRINT f;" ";t
5 IF (f<1 OR f>11) OR (t<1 OR t>11) THEN LET v=1: GO TO 10
6 IF (b$(f)<>c$(p)) OR b$(t)<>"-" THEN LET v=1: GO TO 10
7 FOR i=f TO t STEP SGN(t-f)
8 IF b$(i)="-" THEN IF i<>t THEN LET v=1
9 NEXT i
10 IF v=1 THEN PRINT "INVALID": GO TO 2
11 LET b$(t)=b$(f): LET b$(f)="-"
12 LET r$=IIF$(p=1,"xo+x","ox+o")
13 LET m=MATCH(r,ドルb$): IF m=0 THEN GO TO 18
14 FOR i=m+1 TO POS(c$(p),b,ドルm+2)
15 IF b$(i)=c$(3-p) THEN LET b$(i)="-": DEC s(3-p): END IF
16 NEXT i
17 IF s(3-p)<2 THEN PRINT b$'"P";p;" WINS": STOP
18 INC p,1 TO 2
19 GO TO 2
Output (can't copy from the output widow, so screen shot) enter image description here
enter image description here
C#, 730 bytes
using System;using System.Linq;class P{static void Main(string[]z){int p=1,d,e,g,x,y;var b=new[]{3,1,1,1,1,0,0,0,2,2,2,2};var o=new[]{"_","x","o"};Action<string>h=s=>{Console.Write(s,p);Environment.Exit(0);};Action i=()=>h("INVALID");Func<int,int,bool>j=(q,r)=>b.Select((v,w)=>w<=q||w>=r||v>0?0:w).Any(w=>w>0);Action<int>k=m=>{e=0;for(d=1;d<12;d++){if(b[d]==m){if(e>0&&!j(e,d))for(g=e+1;g<d;g++)b[g]=0;e=d;}}if(b.Count(w=>w>0&&w!=m)<3)h("P{0} WINS");};try{for(;;){for(g=1;g<12;g++)Console.Write(o[b[g]]);var n=Console.ReadLine();if(n=="quit")h("");var c=n.Split(' ');x=int.Parse(c[0]);y=int.Parse(c[1]);if(c.Length>2||b[x]!=p||b[y]!=0||(p>1?y:x)>=(p>1?x:y)||j(x<y?x:y,x<y?y:x))i();b[x]=0;b[y]=p;k(p);p=p>1?1:2;k(p);}}catch{i();}}}
I imagine that further improvements are possible. On the other hand, I interpreted the INVALID output as ending execution, so I may need to fix that issue to be in parity with other answers.