/* _ |_) |_)ATTLESHIP Authored by abakh No rights are reserved and this software comes with no warranties of any kind to the extent permitted by law. compile with -lncurses */ #include #include #include #include #include #include #include #include "config.h" #define MISS -2 #define SEA -1 #define HIT 0 #define ENGLISH_LETTERS 26 #define NOTHING -1 #define ALL 0x7c #define RED 3 #define CYAN 2 typedef signed char byte; typedef unsigned char bitbox; bool multiplayer; byte py,px;//cursor chtype colors[4]={0}; byte game[2][10][10];//main board bool computer[2] = {0}; byte score[2] = {0};//set by header() bitbox sunk[2]={0}; byte just_sunk[2]={0};//to be displayed for human players byte firstinrowy , firstinrowx ; byte lastinrowy ,lastinrowx; byte goindirection; byte shotinvain; void sigint_handler(int x){ endwin(); puts("Quit."); exit(x); } void mouseinput(bool ingame){ #ifndef NO_MOUSE MEVENT minput; #ifdef PDCURSES nc_getmouse(&minput); #else getmouse(&minput); #endif if(minput.bstate & (BUTTON1_CLICKED|BUTTON1_RELEASED)){ if( minput.y-4 < 10){ if( (ingame && minput.x-23<20 && minput.x-23>=0 ) || (!ingame && minput.x-1<20) ){//it most be on the trackboard if ingame is true py=minput.y-4; px=(minput.x-1-(ingame*2)) /2; } } else return; } if(minput.bstate & (BUTTON1_CLICKED|BUTTON1_RELEASED)) ungetch('\n'); if(minput.bstate & (BUTTON2_CLICKED|BUTTON2_RELEASED|BUTTON3_CLICKED|BUTTON3_RELEASED) ) ungetch('r'); #endif } void rectangle(byte sy,byte sx){ for(byte y=0;y<=10+1;++y){ mvaddch(sy+y,sx,ACS_VLINE); mvaddch(sy+y,sx+10*2,ACS_VLINE); } for(byte x=0;x<=10*2;++x){ mvaddch(sy,sx+x,ACS_HLINE); mvaddch(sy+10+1,sx+x,ACS_HLINE); } mvaddch(sy,sx,ACS_ULCORNER); mvaddch(sy+10+1,sx,ACS_LLCORNER); mvaddch(sy,sx+10*2,ACS_URCORNER); mvaddch(sy+10+1,sx+10*2,ACS_LRCORNER); } void print_type(byte type){ switch(type){ case(2): addstr("patrol boat"); break; case(3): addstr("destroyer"); break; case(4): addstr("battleship"); break; case(5): addstr("carrier"); break; case(6): addstr("submarine"); break; } } void MID(byte *y , byte *x, byte direction){ switch(direction){ case(0): *x=*x-1; break; case(1): *y=*y-1; break; case(2): *x=*x+1; break; case(3): *y=*y+1; break; } } void genocide(bool side , byte type){ byte y,x; for(y=0;y<10;++y){ for(x=0;x<10;++x){ if(game[side][y][x] == type) game[side][y][x] = SEA; } } } void header(bool side){ score[0]=score[1]=0; byte y,x; for(y=0;y<10;++y){ for(x=0;x<10;++x){ if(game[!side][y][x] == HIT) score[side]++; if(game[side][y][x] == HIT) score[!side]++; } } mvaddch(0,1, '_'); mvprintw(1,0,"|_) %2d:%2d",score[side],score[!side]); mvprintw(2,0,"|_)ATTLESHIP "); if(multiplayer){ attron(colors[side]); if(side) printw("Yellow's turn"); else printw("Green's turn"); attroff(colors[side]); } } void draw(bool side,byte sy,byte sx,bool regular){//the game's board rectangle(sy,sx); chtype ch ; byte y,x; for(y=0;y<10;++y){ for(x=0;x<10;++x){ ch =A_NORMAL; if(y==py && x==px) ch |= A_STANDOUT; if(game[side][y][x] == HIT) ch |= 'X'|colors[RED]; else if(game[side][y][x] > 0 && !(multiplayer&®ular) ) ch |= ACS_BLOCK|colors[side]; else if(game[side][y][x]== MISS) ch |= 'O'|colors[CYAN]; else if(!(multiplayer&®ular)) ch |= '~'|colors[CYAN]; else ch |=' '; mvaddch(sy+1+y,sx+x*2+1,ch); } } } void draw_trackboard(bool side,byte sy,byte sx){ rectangle(sy,sx); chtype ch ; byte y,x; for(y=0;y<10;++y){ for(x=0;x<10;++x){ ch =A_NORMAL; if(y==py && x==px-10) ch |= A_STANDOUT; if(game[!side][y][x] == HIT) ch |= '*'|colors[RED]; else if(game[!side][y][x]== MISS) ch |= '~'|colors[CYAN]; else ch |= '.'; mvaddch(sy+1+y,sx+x*2+1,ch); } } refresh(); } void autoset(bool side){ byte y=0,x=0,direction=0, invain=0; byte realy,realx; byte l; for(byte type=2;type<7;++type){ SetLocation: realy=rand()%10; realx=rand()%10; invain=0; SetDirection: y=realy; x=realx; direction=rand()%4; for(l=0;(type != 6 && l=10 || x>=10 || game[side][y][x] != SEA ){ genocide(side,type); ++invain; direction= (direction+1)%4; if(invain<4) goto SetDirection; else goto SetLocation;//endless loop } else{ game[side][y][x]=type; MID(&y,&x,direction); } } } } void set_the_board(bool side){ if( computer[side] ){ autoset(side); return; } erase(); mvaddch(0,1, '_'); mvaddstr(1,0,"|_) Set your board"); mvaddstr(2,0,"|_)ATTLESHIP"); mvaddstr(16,0,"Press RETURN to specify the location and press R to rotate the ship."); int input; byte y=0,x=0,direction=0, invain=0; byte realy,realx; byte l; py=px=0; for(byte type=2;type<7;++type){ mvaddstr(15,0,"Put your "); print_type(type); addstr(" in its position: "); SetLocation: while(1){ draw(side,3,0,false); refresh(); input = getch(); if( input == KEY_MOUSE ) mouseinput(0); if( (input=='k' || input==KEY_UP) && py>0) --py; if( (input=='j' || input==KEY_DOWN) && py<9) ++py; if( (input=='h' || input==KEY_LEFT) && px>0) --px; if( (input=='l' || input==KEY_RIGHT) && px<9) ++px; if( input=='\n'||input==KEY_ENTER ) break; if( input=='q' ) sigint_handler(EXIT_SUCCESS); } realy=y=py; realx=x=px; invain=0; SetDirection: y=realy; x=realx; for(l=0;(type != 6 && l=10 || x>=10 || game[side][y][x] != SEA ){ genocide(side,type); ++invain; direction= (direction+1)%4; if(invain<4) goto SetDirection; else goto SetLocation;//endless loop } else{ game[side][y][x]=type; MID(&y,&x,direction); } } while(1){ invain=0; draw(side,3,0,false); input=getch(); if( input== 'r' || input == 'R' ){ genocide(side,type); direction= (direction+1)%4; goto SetDirection; } else if(input == KEY_MOUSE) mouseinput(0); else break; } } } void turn_shift(void){ if(!multiplayer) return; char key = 'a'+(rand()%ENGLISH_LETTERS); int input1,input2,input3; input1=input2=input3=0; erase(); beep(); mvaddch(0,1, '_'); mvaddstr(1,0,"|_) Anti-cheater"); mvaddstr(2,0,"|_)ATTLESHIP"); mvaddstr(4,0,"********************"); mvprintw(5,0," Type '%c' 3 times ",key); mvaddstr(6,0," before "); mvaddstr(7,0," proceeding "); mvaddstr(8,0," to the game "); mvaddstr(10,0,"********************"); refresh(); while(1){ input3=input2; input2=input1; input1=getch(); if( (input1==input2) && (input2==input3) && (input3==key) ) break; } erase(); } byte shoot(bool turn, byte y , byte x){ if( y<0 || x<0 || y>9 || x>9 ){ //didn't shoot at all return NOTHING; } byte s = game[!turn][y][x]; if(s==HIT || s==MISS) return NOTHING; if(s>0){ game[!turn][y][x]=HIT; return 1; } else{ game[!turn][y][x]=MISS; return 0; } } void sink_announce(bool side){ byte type,y,x; for(type=2;type<7;++type){ for(y=0;y<10;++y){ for(x=0;x<10;++x){ if( game[!side][y][x] == type ) goto Next; } } //there is no instance of 'type' in the opponet's board if( ( (1 << type) | sunk[!side] ) != sunk[!side] ){//if it is not yet announced as sunk sunk[!side] |= (1 << type); if(computer[side]){ lastinrowy=lastinrowx=firstinrowy=firstinrowx=-1; shotinvain=0; } else{ just_sunk[!side]=type;//leave to be displayed by you_sunk } return; } Next: continue; } } void you_sunk(bool side){ if( just_sunk[!side] == 3) mvaddstr(15,0,"You have destroyed my destroyer!!"); else if( just_sunk[!side]){ mvaddstr(15,0,"You have sunk my "); print_type(just_sunk[!side]); addstr("!!"); } just_sunk[!side]=0; } void cheat(bool side){ /* its actually an anti-cheat, the player can place all their ships adjacent to one another and in the same direction, and the algorithm will often play in a way that it will be left with one or two isolated tiles being unshot (with their respective ships being shot before). in a such a situation a human will *very easily* find the tiles with logical thinking, but the computer shoots randomly and it will take such a long time for it that it will often lose the winning game. this function still doesn't make a win,it's randomly executed. if i implemented the logical thinking thing, it would become a difficult, unenjoyable game.*/ byte y,x; for(y=0;y<10;++y){ for(x=0;x<10;++x){ if(game[!side][y][x]>0){ shoot(side,y,x); firstinrowy=y; firstinrowx=x; return; } } } } void decide(bool side){// sink_announce is responsible for unsetting the global variables involved byte y,x,r; Again: if( firstinrowy == NOTHING ){ if( score[side] > 14 && score[side]9 || x>9) && r==NOTHING){ goto Again; } } r= shoot(side,y,x);//(y,x) may be MISS, but its impossible for it to be empty water, as executing this means it has tested every direction before if(r==1){ lastinrowy=y;//continue from the imaginary firstinrow lastinrowx=x; } if(r==NOTHING) goto Again; } } else{ lastinrowy= y; lastinrowx= x; } if( r != NOTHING ) return; } } else{ y=lastinrowy; x=lastinrowx; MID(&y,&x,goindirection); r=shoot(side,y,x); if( r == 1 ){ lastinrowy=y; lastinrowx=x; } else{ lastinrowy=lastinrowx=NOTHING; goindirection=(goindirection+2)%4; } if( r != NOTHING ) return; else{ goto Again; } } } void help(bool side){//side is only there to feed header() erase(); header(side); attron(A_BOLD); mvprintw(3,0," **** THE CONTROLS ****"); mvprintw(9,0,"YOU CAN ALSO USE THE MOUSE!"); attroff(A_BOLD); mvprintw(4,0,"RETURN/ENTER : Shoot"); mvprintw(5,0,"R : Rotate"); mvprintw(6,0,"hjkl/ARROW KEYS : Move cursor"); mvprintw(7,0,"q : Quit"); mvprintw(8,0,"F1 & F2 : Help on controls & gameplay"); mvprintw(11,0,"Press a key to continue"); getch(); erase(); } void gameplay(bool side){//side is only there to feed header() erase(); header(side); attron(A_BOLD); mvprintw(3,0," **** THE GAMEPLAY ****"); attroff(A_BOLD); move(4,0); printw("Guess the location of your opponet's\n"); printw("ships and sink them! The player\n"); printw("who sinks all the opponet's ships wins."); getch(); erase(); } int main(void){ initscr(); #ifndef NO_MOUSE mousemask(ALL_MOUSE_EVENTS,NULL); #endif curs_set(0); noecho(); cbreak(); keypad(stdscr,1); if( has_colors() ){ start_color(); use_default_colors(); init_pair(1,COLOR_GREEN,-1); init_pair(2,COLOR_YELLOW,-1); init_pair(3,COLOR_CYAN,-1); init_pair(4,COLOR_RED,-1); for(byte b=0;b<4;++b) colors[b]=COLOR_PAIR(b+1); } int input; printw("Choose type of the game:\n"); printw("1 : Single Player*\n"); printw("2 : Multi Player\n"); refresh(); input=getch(); if(input == '2'){ multiplayer=1; computer[1]=computer[0]=0; } else{ multiplayer=0; computer[1]=1; computer[0]=0; } Start: firstinrowy=firstinrowx=lastinrowy=lastinrowx=goindirection=NOTHING; shotinvain=0; sunk[0]=sunk[1]=0; memset(game,SEA,200); srand(time(NULL)%UINT_MAX); erase(); set_the_board(0); turn_shift(); set_the_board(1); bool won; bool turn=1; Turn: px=10; py=0; sink_announce(turn); if( sunk[0]==ALL ){ won=1; goto End; } else if( sunk[1]==ALL ){ won=0; goto End; } //the turn starts HERE turn=!turn; //turn_shift(); if( computer[turn] ){ decide(turn); goto Turn; } else{ erase(); you_sunk(turn); while(1){ header(turn); draw(turn,3,0,true); draw_trackboard(turn,3,22); refresh(); input=getch(); if(input == KEY_F(1) || input=='?' ) help(turn); if(input == KEY_F(2) ) gameplay(turn); if(input == KEY_MOUSE) mouseinput(1); if( (input=='k' || input==KEY_UP) && py>0) --py; if( (input=='j' || input==KEY_DOWN) && py<9) ++py; if( (input=='h' || input==KEY_LEFT) && px>10) --px; if( (input=='l' || input==KEY_RIGHT) && px<19) ++px; if( input=='q') sigint_handler(EXIT_SUCCESS); if( input=='\n' || input==KEY_ENTER){ byte r=shoot(turn,py,px-10); if(r != NOTHING){ goto Turn; } } } } End: erase(); header(won); draw(won,3,0,false); draw_trackboard(won,3,22); if( computer[won] ) mvaddstr(15,0,"Hahaha! I won! "); else mvprintw(15,0,"Player %d won the game.",won+1); addstr(" Wanna play again? (y/n)"); refresh(); curs_set(1); input=getch(); if( input!='n' && input !='N' && input!='q' ){ curs_set(0); goto Start; } endwin(); return 0; }