aboutsummaryrefslogtreecommitdiff
path: root/engines/avalanche/nimunit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/avalanche/nimunit.cpp')
-rw-r--r--engines/avalanche/nimunit.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/engines/avalanche/nimunit.cpp b/engines/avalanche/nimunit.cpp
new file mode 100644
index 0000000000..0becd47d10
--- /dev/null
+++ b/engines/avalanche/nimunit.cpp
@@ -0,0 +1,443 @@
+#include "ptoc.h"
+
+/*
+ ÛßÜ ÛßÜ ÜßßÜ ßÛß Ûßß Üß ßÛß ÜßÛßÜ ßÛß ÛÜ Û ÜÛßß ßÛß ÛßÜ Ûßß Û
+ Ûß ÛÛ Û Û Ü Û Ûßß ßÜ Û Û Û Û Û Û ÛÜÛ ßßßÜ Û ÛÛ Ûßß Û
+ ß ß ß ßß ßß ßßß ß ß ß ß ß ßßß ß ßß ßßß ß ß ß ßßß ßßß
+
+ NIM UNIT A unit version of the pub game (Nim). */
+
+#define __nimunit_implementation__
+
+
+#include "nimunit.h"
+
+
+const array<false,true,varying_string<7> > names = {{"Avalot","Dogfood"}};
+
+array<1,3,byte> old,stones;
+array<0,3,0,22,1,7,byte> stonepic; /* picture of Nimstone */
+byte turns;
+boolean dogfoodsturn; byte fv; byte stonesleft;
+
+boolean clicked;
+
+byte row,number;
+
+boolean squeak;
+shortint mnum,mrow;
+
+void chalk(integer x,integer y, string z)
+{
+ const array<0,3,byte> greys = {{0,8,7,15}};
+ byte fv;
+
+ for( fv=0; fv <= 3; fv ++)
+ {
+ setcolor(greys[fv]);
+ outtextxy(x-fv,y,z);
+ sound(fv*100*length(z)); delay(3); nosound; delay(30);
+ }
+}
+
+void setup()
+{
+ const integer page3 = 0xac00;
+ byte gd,gm;
+ untyped_file f;
+ byte bit;
+
+ setactivepage(3);
+ setvisualpage(3);
+ cleardevice();
+ dawn();
+
+ assign(f,"nim.avd");
+ reset(f,1);
+ seek(f,41);
+ for( gm=0; gm <= 22; gm ++)
+ for( bit=0; bit <= 3; bit ++)
+ {
+ port[0x3c4]=2; port[0x3ce]=4; port[0x3c5]=1 << bit; port[0x3cf]=bit;
+ blockread(f,stonepic[bit][gm],7);
+ }
+ for( gd=1; gd <= 3; gd ++)
+ for( gm=0; gm <= 22; gm ++)
+ for( bit=0; bit <= 3; bit ++)
+ {
+ port[0x3c4]=2; port[0x3ce]=4; port[0x3c5]=1 << bit; port[0x3cf]=bit;
+ blockread(f,mem[page3*3200+gd*2800+gm*80],7);
+ }
+ for( gm=0; gm <= 36; gm ++)
+ for( bit=0; bit <= 3; bit ++)
+ {
+ port[0x3c4]=2; port[0x3ce]=4; port[0x3c5]=1 << bit; port[0x3cf]=bit;
+ blockread(f,mem[page3*400+49+gm*80],30);
+ }
+ close(f);
+
+ gd=getpixel(0,0); /* clear codes */
+ setcolor(4); rectangle(394,50,634,197);
+ setfillstyle(1,6); bar(395,51,633,196);
+ rectangle(10,5,380,70); bar(11,6,379,69);
+ setcolor(15);
+ outtextxy(475,53,"SCOREBOARD:");
+ setcolor(14);
+ outtextxy(420,63,"Turn:");
+ outtextxy(490,63,"Player:");
+ outtextxy(570,63,"Move:");
+
+ for( gd=1; gd <= 3; gd ++) stones[gd]=gd+2;
+
+ turns=0; dogfoodsturn=true;
+
+ chalk(27,15,"Take pieces away with:");
+ chalk(77,25,"1) the mouse (click leftmost)");
+ chalk(53,35,"or 2) the keyboard:");
+ chalk(220,35,string('\30')+'/'+'\31'+": choose row,");
+ chalk(164,45,string("+/- or ")+'\33'+'/'+'\32'+": more/fewer,");
+ chalk(204,55,"Enter: take stones.");
+
+ row=1; number=1; fillchar(old,sizeof(old),'\0'); stonesleft=12;
+
+ /* Set up mouse. */
+ off_virtual();
+ oncandopageswap=false;
+
+ setactivepage(3);
+ setvisualpage(3);
+}
+
+void plotstone(byte x,byte y)
+{
+ byte fv,bit; word ofs;
+
+ ofs=3200+y*2800+x*8;
+ for( fv=0; fv <= 22; fv ++)
+ for( bit=0; bit <= 3; bit ++)
+ {
+ port[0x3c4]=2; port[0x3ce]=4; port[0x3c5]=1 << bit; port[0x3cf]=bit;
+ move(stonepic[bit][fv],mem[0xac00*ofs+fv*80],7);
+ }
+}
+
+void board()
+{
+ byte fv,ff;
+
+ for( fv=1; fv <= 3; fv ++)
+ for( ff=1; ff <= stones[fv]; ff ++)
+ plotstone(ff,fv);
+}
+
+void startmove()
+{
+ varying_string<2> tstr; integer ypos;
+
+ turns += 1; str(turns,2,tstr); ypos=63+turns*10;
+ dogfoodsturn=! dogfoodsturn;
+ chalk(433,ypos,tstr);
+ chalk(493,ypos,names[dogfoodsturn]);
+ old=stones;
+}
+
+void show_changes()
+{
+ byte fv,ff,fq; varying_string<2> move;
+
+ move=string(chr(64+row))+chr(48+number);
+ chalk(573,63+turns*10,move);
+ log_aside(names[dogfoodsturn]+" plays "+move+'.');
+
+ for( fv=1; fv <= 3; fv ++)
+ if (old[fv]>stones[fv])
+ for( ff=stones[fv]+1; ff <= old[fv]; ff ++)
+ for( fq=0; fq <= 22; fq ++) fillchar(mem[0xac00*3200+fv*2800+ff*8+fq*80],7,'\0');
+ stonesleft -= number;
+}
+
+void checkmouse();
+
+static void blip() { note(1771); delay(3); nosound; clicked=false; }
+
+void checkmouse()
+{
+ xycheck(); /* Check the mouse */
+ clicked=keystatus>0;
+ if (clicked)
+ {
+ void& with = r;
+ /* The mouse was clicked. Where? */
+ mrow=(my-38) / 35;
+ if ((mrow<1) || (mrow>3)) blip();
+ mnum=stones[mrow]-(mx / 64)+1;
+ if ((mnum<1) || (mnum>(unsigned char)stones[mrow])) blip();
+ }
+}
+
+void takesome();
+
+static void less() { if (number>1) number -= 1; }
+
+void takesome()
+{
+ char r; byte sr;
+
+ number=1;
+ do {
+ do {
+ sr=stones[row];
+ if (sr==0) { row=row % 3+1; number=1; }
+ } while (!(sr!=0));
+ if (number>sr) number=sr;
+ setcolor(1); rectangle(63+(sr-number)*64,38+35*row,54+sr*64,63+35*row);
+ /* Wait for choice */
+ on();
+ do { checkmouse(); } while (!(keypressed() || clicked));
+ if (keypressed()) r=upcase(readkey());
+ off();
+
+ setcolor(0); rectangle(63+(sr-number)*64,38+35*row,54+sr*64,63+35*row);
+
+ if (clicked)
+ {
+ number=mnum;
+ row=mrow;
+ return;
+ } else
+ {
+ switch (r) {
+ case '\0': switch (readkey()) {
+ case 'H': if (row>1) row -= 1; break; /* Up */
+ case 'P': if (row<3) row += 1; break; /* Down */
+ case 'K': number += 1; break;
+ case 'M': less(); break;
+ case 'I': row=1; break; /* PgUp */
+ case 'Q': row=3; break; /* PgDn */
+ case 'G': number=5; break; /* Home- check routine will knock this down to size */
+ case 'O': number=1; break; /* End */
+ }
+ break;
+ case '+': number += 1; break;
+ case '-': less(); break;
+ case RANGE_3('A','C'): row=ord(r)-64;
+ break;
+ case RANGE_5('1','5'): number=ord(r)-48;
+ break;
+ case '\15': return; break; /* Enter was pressed */
+ }
+ }
+ } while (!false);
+}
+
+void endofgame()
+{
+ char rr;
+
+ chalk(595,63+turns*10,"Wins!");
+ outtextxy(100,190,"- - - Press any key... - - -");
+ while (keypressed()) rr=readkey();
+ do { check(); } while (!(mpress==0));
+
+ { void& with = r; do { check(); } while (!(keypressed() || (mrelease>0)));}
+ if (keypressed()) rr=readkey();
+
+ mousepage(cp);
+ off();
+ on_virtual();
+}
+
+void dogfood();
+ /* AI procedure to play the game */
+const matrix<1,3,1,2,byte> other = {{{{2,3}},{{1,3}},{{1,2}}}};
+
+
+static byte live,fv,ff,matches,thisone,where;
+
+static array<1,3,byte> r,sr;
+static array<1,3,boolean> inap;
+
+static boolean lmo; /* Let Me Out! */
+
+static byte ooo; /* Odd one out */
+
+
+static boolean find(byte x)
+ /* This gives True if there's a pile with x stones in. */
+{
+ boolean q; byte p;
+
+ boolean find_result;
+ q=false;
+ for( p=1; p <= 3; p ++) if (stones[p]==x) { q=true; inap[p]=true; }
+ find_result=q;
+ return find_result;
+}
+
+
+
+static void find_ap(byte start,byte stepsize)
+{
+ byte ff;
+
+ matches=0;
+ fillchar(inap,sizeof(inap),'\0'); /* blank 'em all */
+ for( ff=0; ff <= 2; ff ++) if (find(start+ff*stepsize)) matches += 1;
+ else thisone=ff;
+
+ /* Now.. Matches must be 0, 1, 2, or 3.
+ 0/1 mean there are no A.P.s here, so we'll keep looking,
+ 2 means there is a potential A.P. that we can create (ideal!), and
+ 3 means that we're already in an A.P. (Trouble!). */
+
+ switch (matches) {
+ case 2: {
+ for( ff=1; ff <= 3; ff ++) /* find which one didn't fit the A.P. */
+ if (! inap[ff]) ooo=ff;
+ if (stones[ooo]>(start+thisone*stepsize)) /* check it's possible! */
+ { /* create an A.P. */
+ row=ooo; /* already calculated */
+ /* Start+thisone*stepsize will give the amount we SHOULD have here. */
+ number=stones[row]-(start+thisone*stepsize); lmo=true; return;
+ }
+ }
+ break;
+ case 3: { /* we're actually IN an A.P! Trouble! Oooh dear. */
+ row=r[3]; number=1; lmo=true; return; /* take 1 from the largest pile */
+ }
+ break;
+ }
+}
+
+void dogfood()
+{
+ boolean sorted; byte temp;
+
+
+ live=0; lmo=false;
+ for( fv=1; fv <= 3; fv ++)
+ {
+ if (stones[fv]>0)
+ {
+ live += 1;
+ r[live]=fv; sr[live]=stones[fv];
+ }
+ }
+ switch (live) {
+ case 1: /* Only one is free- so take 'em all */
+ { row=r[1]; number=stones[r[1]]; return; }
+ break;
+ case 2: /* Two are free- make them equal */
+ {
+ if (sr[1]>sr[2])
+ { row=r[1]; number=sr[1]-sr[2]; return; } else /* T > b */
+ if (sr[1]<sr[2])
+ { row=r[2]; number=sr[2]-sr[1]; return; } else /* B > t */
+ { row=r[1]; number=1; return; } /* B = t... oh no, we've lost! */
+ }
+ break;
+ case 3: /* Ho hum... this'll be difficult! */
+ {
+ /* There are three possible courses of action when we have 3 lines left:
+ 1) Look for 2 equal lines, then take the odd one out.
+ 2) Look for A.P.s, and capitalise on them.
+ 3) Go any old where. */
+
+ for( fv=1; fv <= 3; fv ++) /* Look for 2 equal lines */
+ if (stones[other[fv][1]]==stones[other[fv][2]])
+ {
+ row=fv; /* this row */ number=stones[fv]; /* all of 'em */ return;
+ }
+
+ do {
+ sorted=true;
+ for( fv=1; fv <= 2; fv ++)
+ if (sr[fv]>sr[fv+1])
+ {
+ temp=sr[fv+1]; sr[fv+1]=sr[fv]; sr[fv]=temp;
+ temp= r[fv+1]; r[fv+1]= r[fv]; r[fv]=temp;
+ sorted=false;
+ }
+ } while (!sorted);
+ /* Now we look for A.P.s ... */
+ for( fv=1; fv <= 3; fv ++)
+ {
+ find_ap(fv,1); /* there are 3 "1"s */
+ if (lmo) return; /* cut-out */
+ }
+ find_ap(1,2); /* only "2" possible */
+ if (lmo) return;
+
+ /* A.P. search must have failed- use the default move. */
+ row=r[3]; number=1; return;
+ }
+ break;
+ }
+}
+
+void play_nim() /* Plays the game. Only procedure in this unit to
+ be declared in the interface section. */
+{
+ byte groi;
+
+ if (dna.wonnim)
+ { /* Already won the game. */
+ dixi('Q',6);
+ return;
+ }
+
+ if (! dna.asked_dogfood_about_nim)
+ {
+ dixi('q',84);
+ return;
+ }
+
+ dixi('Q',3);
+ dna.playednim += 1;
+ dusk();
+ oncandopageswap=false;
+ copypage(3,1-cp); /* Store old screen. */ groi=getpixel(0,0);
+ off();
+
+ setup();
+ board();
+ on();
+ mousepage(3);
+
+ do {
+ startmove();
+ if (dogfoodsturn) dogfood(); else takesome();
+ stones[row] -= number;
+ show_changes();
+ } while (!(stonesleft==0));
+ endofgame(); /* Winning sequence is A1, B3, B1, C1, C1, btw. */
+
+ dusk(); off();
+ oncandopageswap=true;
+ copypage(1-cp,3); /* Restore old screen. */ groi=getpixel(0,0);
+ on(); dawn();
+
+ if (dogfoodsturn)
+ { /* Dogfood won - as usual */
+ log_aside("He won.");
+ if (dna.playednim==1) /* Your first game */
+ dixi('Q',4); /* Goody! Play me again? */
+ else
+ dixi('Q',5); /* Oh, look at that! I've won again! */
+ pennycheck(4); /* And you've just lost 4d! */
+ } else
+ { /* You won - strange! */
+ log_aside("You won.");
+ dixi('Q',7); /* You won! Give us a lute! */
+ dna.obj[lute]=true;
+ objectlist();
+ dna.wonnim=true;
+ show_one(1); /* Show the settle with no lute on it. */
+ points(7); /* 7 points for winning! */
+ }
+
+ if (dna.playednim==1) points(3); /* 3 points for playing your 1st game. */
+
+}
+
+ /* No init part. */