diff options
Diffstat (limited to 'engines/avalanche/nimunit.cpp')
-rw-r--r-- | engines/avalanche/nimunit.cpp | 443 |
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. */ |