mirror of
https://github.com/abakh/nbsdgames.git
synced 2024-12-04 14:46:22 -05:00
404 lines
10 KiB
C
404 lines
10 KiB
C
#include <curses.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#define NOTHING 123
|
|
/*
|
|
_ _ _
|
|
(_'| |(_'
|
|
._):_:._)
|
|
|
|
|
|
copyright Hossein Bakhtiarifar 2018 (c)
|
|
No rights are reserved and this software comes with no warranties of any kind to the extent permitted by law.
|
|
|
|
compile with -lncurses
|
|
*/
|
|
typedef signed char byte;
|
|
int len,wid,py,px;
|
|
chtype colors[6]={A_BOLD};
|
|
int score[2] ={0};
|
|
int computer[2]={0};
|
|
char so[2] = {'S','O'};
|
|
|
|
char rd(char board[len][wid],int y, int x){
|
|
if(y<0 || x<0 || y>= len || x>=wid)
|
|
return NOTHING;
|
|
else
|
|
return board[y][x];
|
|
}
|
|
void color(byte colored[len][wid],int y,int x,bool side){
|
|
if(colored[y][x] == !side || colored[y][x]==2)
|
|
colored[y][x]=2;
|
|
else
|
|
colored[y][x]=side;
|
|
}
|
|
void rectangle(int sy,int sx){
|
|
for(int y=0;y<=len+1;y++){
|
|
mvaddch(sy+y,sx,ACS_VLINE);
|
|
mvaddch(sy+y,sx+wid*2,ACS_VLINE);
|
|
}
|
|
for(int x=0;x<=wid*2;x++){
|
|
mvaddch(sy,sx+x,ACS_HLINE);
|
|
mvaddch(sy+len+1,sx+x,ACS_HLINE);
|
|
}
|
|
mvaddch(sy,sx,ACS_ULCORNER);
|
|
mvaddch(sy+len+1,sx,ACS_LLCORNER);
|
|
mvaddch(sy,sx+wid*2,ACS_URCORNER);
|
|
mvaddch(sy+len+1,sx+wid*2,ACS_LRCORNER);
|
|
}
|
|
|
|
void draw(int sy,int sx,char board[len][wid],byte colored[len][wid]){
|
|
rectangle(sy,sx);
|
|
chtype attr ;
|
|
char prnt;
|
|
int y,x;
|
|
for(y=0;y<len;y++){
|
|
for(x=0;x<wid;x++){
|
|
attr=A_NORMAL;
|
|
if(y==py && x==px)
|
|
attr |= A_STANDOUT;
|
|
if(colored[y][x]>=0)
|
|
attr |= colors[colored[y][x]];
|
|
if( board[y][x] )
|
|
prnt = board[y][x];
|
|
else
|
|
prnt = '_';
|
|
mvaddch(sy+1+y,sx+x*2+1,attr|prnt);
|
|
}
|
|
}
|
|
}
|
|
|
|
byte did_sos(char board[len][wid], int y , int x ){
|
|
byte dy,dx;
|
|
byte soses=0;
|
|
if(board[y][x]== 'S'){
|
|
for(dy=-1;dy<2;dy++){
|
|
for(dx=-1;dx<2;dx++){
|
|
if(rd(board,y+dy,x+dx)=='O' && rd(board,y+2*dy,x+2*dx) == 'S' )
|
|
soses++;
|
|
}
|
|
}
|
|
return soses;
|
|
}
|
|
else if(board[y][x]== 'O'){
|
|
for(dy=-1;dy<2;dy++){
|
|
for(dx=-1;dx<2;dx++){
|
|
if(rd(board,y+dy,x+dx)=='S' && rd(board,y-dy,x-dx) =='S')
|
|
soses++;
|
|
}
|
|
}
|
|
return soses/2;
|
|
}
|
|
return 0;
|
|
}
|
|
void color_sos(char board[len][wid],byte colored[len][wid], int y , int x ,bool side){
|
|
byte dy,dx;
|
|
if(board[y][x]== 'S'){
|
|
for(dy=-1;dy<2;dy++){
|
|
for(dx=-1;dx<2;dx++){
|
|
if(rd(board,y+dy,x+dx)=='O' && rd(board,y+2*dy,x+2*dx) == 'S' ){
|
|
color(colored,y,x,side);
|
|
color(colored,y+dy,x+dx,side);
|
|
color(colored,y+2*dy,x+2*dx,side);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(board[y][x]== 'O'){
|
|
for(dy=-1;dy<2;dy++){
|
|
for(dx=-1;dx<2;dx++){
|
|
if(rd(board,y+dy,x+dx)=='S' && rd(board,y-dy,x-dx) =='S'){
|
|
color(colored,y,x,side);
|
|
color(colored,y+dy,x+dx,side);
|
|
color(colored,y-dy,x-dx,side);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void randmove(int* y,int* x,byte* c){
|
|
*y=random()%len;
|
|
*x=random()%wid;
|
|
*c=random()%2;
|
|
}
|
|
int decide ( char board[len][wid],byte colored[len][wid], byte depth , byte side ){ //the move is imaginary if side is negative
|
|
int adv,bestadv;
|
|
int oppadv;
|
|
int besty,bestx;
|
|
char bestchar;
|
|
byte c;
|
|
oppadv=adv=bestadv=INT_MIN;
|
|
besty=bestx=-1;
|
|
int y,x;
|
|
|
|
int ry,rx;
|
|
byte rc;
|
|
randmove(&ry,&rx,&rc);//provides efficient randomization
|
|
for(y=0;y<len;y++){
|
|
for(x=0;x<wid;x++){
|
|
if(!board[y][x]){
|
|
for(c=0;c<2;c++){
|
|
board[y][x]=so[c];
|
|
adv=did_sos(board,y,x);
|
|
if(depth>0)
|
|
oppadv= decide(board,NULL,depth-1,-1);
|
|
if(depth>0 && oppadv != INT_MIN)//this has no meanings if the opponet cannot move
|
|
adv-=1*oppadv;
|
|
if(besty<0 ||adv>bestadv || (adv==bestadv && y==ry && x==rx && c==rc /*c==0*/) ){
|
|
bestadv=adv;
|
|
besty=y;
|
|
bestx=x;
|
|
bestchar=so[c];
|
|
}
|
|
board[y][x]=0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(besty>=0 && side >= 0 ){
|
|
board[besty][bestx]=bestchar;
|
|
score[side]+= did_sos(board,besty,bestx);
|
|
color_sos(board,colored,besty,bestx,side);
|
|
}
|
|
return bestadv;
|
|
}
|
|
bool isfilled(char board[len][wid]){
|
|
int y,x;
|
|
for(y=0;y<len;y++)
|
|
for(x=0;x<wid;x++)
|
|
if(!board[y][x])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
void sigint_handler(int x){
|
|
endwin();
|
|
puts("Quit.");
|
|
exit(x);
|
|
}
|
|
void mouseinput(int sy,int sx){
|
|
MEVENT minput;
|
|
getmouse(&minput);
|
|
if( minput.y-4-sy <len && minput.x-1-sx<wid*2){
|
|
py=minput.y-4-sy;
|
|
px=(minput.x-1-sx)/2;
|
|
}
|
|
else
|
|
return;
|
|
if(minput.bstate & BUTTON1_CLICKED)
|
|
ungetch('S');
|
|
if(minput.bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED) )
|
|
ungetch('O');
|
|
}
|
|
void help(void){
|
|
erase();
|
|
mvprintw(0,0," _ _ _");
|
|
mvprintw(1,0,"(_'| |(_' ");
|
|
mvprintw(2,0,"._):_:._) ");
|
|
attron(A_BOLD);
|
|
mvprintw(3,0," **** THE CONTROLS ****");
|
|
mvprintw(9,0,"YOU CAN ALSO USE THE MOUSE!");
|
|
attroff(A_BOLD);
|
|
mvprintw(4,0,"hjkl/ARROW KEYS : Move cursor");
|
|
mvprintw(5,0,"S & O : Write S or O");
|
|
mvprintw(6,0,"q : Quit");
|
|
mvprintw(7,0,"F1 & F2: Help on controls & gameplay");
|
|
mvprintw(8,0,"PgDn,PgUp,<,> : Scroll");
|
|
mvprintw(11,0,"Press a key to continue");
|
|
refresh();
|
|
getch();
|
|
erase();
|
|
}
|
|
void gameplay(void){
|
|
erase();
|
|
mvprintw(0,0," _ _ _");
|
|
mvprintw(1,0,"(_'| |(_' ");
|
|
mvprintw(2,0,"._):_:._) ");
|
|
attron(A_BOLD);
|
|
mvprintw(3,0," **** THE GAMEPLAY ****");
|
|
attroff(A_BOLD);
|
|
move(4,0);
|
|
printw("The game is similiar to Tic Tac Toe:\n");
|
|
printw("The players write S and O in the squares\n");
|
|
printw("and making the straight connected sequence\n");
|
|
printw("S-O-S makes you a score; obviously, the\n");
|
|
printw("player with a higher score wins.");
|
|
refresh();
|
|
getch();
|
|
erase();
|
|
}
|
|
int main(int argc, char** argv){
|
|
int dpt=1;
|
|
signal(SIGINT,sigint_handler);
|
|
if(argc>4 || (argc==2 && !strcmp("help",argv[1])) ){
|
|
printf("Usage: %s [len wid [AIpower]]\n",argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if(argc==2){
|
|
puts("Give both dimensions.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if(argc>=3){
|
|
bool lool = sscanf(argv[1],"%d",&len) && sscanf(argv[2],"%d",&wid);
|
|
if(!lool){
|
|
puts("Invalid input.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if(len<3 || wid<3 || len>300 || wid>300){
|
|
puts("At least one of your given dimensions is either too small or too big.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
}
|
|
else{
|
|
len=5;
|
|
wid=6;
|
|
}
|
|
if(argc==4){
|
|
if( !sscanf(argv[3],"%d",&dpt)){
|
|
puts("Invalid input.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if( dpt<1 || dpt>= 127){
|
|
puts("That should be between 1 and 127.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
srandom(time(NULL)%UINT_MAX);
|
|
int input;
|
|
initscr();
|
|
mousemask(ALL_MOUSE_EVENTS,NULL);
|
|
curs_set(0);
|
|
noecho();
|
|
cbreak();
|
|
keypad(stdscr,1);
|
|
printw("Black plays first.\n Choose the type of the blue player(H/c)\n" );
|
|
input=getch();
|
|
if(input=='c'){
|
|
computer[0]=dpt;
|
|
printw("Computer.\n");
|
|
}
|
|
else{
|
|
computer[0]=0;
|
|
printw("Human.\n");
|
|
}
|
|
printw("Choose the type of the yellow player(h/C)\n");
|
|
input=getch();
|
|
if(input=='h'){
|
|
computer[1]=0;
|
|
printw("Human.\n");
|
|
}
|
|
else{
|
|
computer[1]=dpt;
|
|
printw("Computer.\n");
|
|
}
|
|
if(has_colors()){
|
|
start_color();
|
|
use_default_colors();
|
|
init_pair(1,COLOR_BLUE,-1);
|
|
init_pair(2,COLOR_YELLOW,-1);
|
|
init_pair(3,COLOR_GREEN,-1);
|
|
for(byte b= 0;b<6;b++){
|
|
colors[b]=COLOR_PAIR(b+1);
|
|
}
|
|
|
|
}
|
|
int sy,sx;
|
|
Start:
|
|
sy=sx=0;//for scrolling
|
|
py=px=0;
|
|
char board[len][wid];
|
|
byte colored[len][wid];
|
|
bool t=1;
|
|
score[0]=score[1]=0;
|
|
memset(board,0,len*wid);
|
|
memset(colored,-1,len*wid);
|
|
Turn:
|
|
erase();
|
|
mvprintw(sy+0,sx+0," _ _ _");
|
|
mvprintw(sy+1,sx+0,"(_'| |(_' %d vs %d \n",score[0],score[1]);
|
|
mvprintw(sy+2,sx+0,"._):_:._) \n");
|
|
draw(sy+3,sx+0,board,colored);
|
|
if( isfilled(board) )
|
|
goto End;
|
|
refresh();
|
|
t=!t;
|
|
if(computer[t]){
|
|
mvprintw(sy+len+5,sx+0,"Thinking...");
|
|
refresh();
|
|
decide(board,colored,dpt,t);
|
|
goto Turn;
|
|
}
|
|
//else
|
|
while(1){
|
|
erase();
|
|
mvprintw(sy+0,sx+0," _ _ _");
|
|
mvprintw(sy+1,sx+0,"(_'| |(_' %d vs %d \n",score[0],score[1]);
|
|
mvprintw(sy+2,sx+0,"._):_:._) \n");
|
|
draw(sy+3,sx+0,board,colored);
|
|
refresh();
|
|
input = getch();
|
|
if( input==KEY_PPAGE && LINES< len+3){//the board starts in 3
|
|
sy+=10;
|
|
if(sy>0)
|
|
sy=0;
|
|
}
|
|
if( input==KEY_NPAGE && LINES< len+3){
|
|
sy-=10;
|
|
if(sy< -(len+3) )
|
|
sy=-(len+3);
|
|
}
|
|
if( input=='<' && COLS< wid*2+1){
|
|
sx+=10;
|
|
if(sx>0)
|
|
sx=0;
|
|
}
|
|
if( input=='>' && COLS< wid*2+1){
|
|
sx-=10;
|
|
if(sx< -(wid*2+1))
|
|
sx=-(wid*2+1);
|
|
}
|
|
if( input==KEY_F(1) || input=='?')
|
|
help();
|
|
if( input==KEY_F(2) )
|
|
gameplay();
|
|
if( input==KEY_MOUSE )
|
|
mouseinput(sy,sx);
|
|
if( (input=='k' || input==KEY_UP) && py>0)
|
|
py--;
|
|
if( (input=='j' || input==KEY_DOWN) && py<len-1)
|
|
py++;
|
|
if( (input=='h' || input==KEY_LEFT) && px>0)
|
|
px--;
|
|
if( (input=='l' || input==KEY_RIGHT) && px<wid-1)
|
|
px++;
|
|
if( input=='q')
|
|
sigint_handler(0);
|
|
if(!board[py][px] && (input=='s'||input=='S'||input=='o'||input=='O') ){
|
|
if(input=='s'||input=='S')
|
|
board[py][px]='S';
|
|
else
|
|
board[py][px]='O';
|
|
score[t]+=did_sos(board,py,px);
|
|
color_sos(board,colored,py,px,t);
|
|
goto Turn;
|
|
}
|
|
}
|
|
End:
|
|
if( score[1] == score[0])
|
|
mvprintw(sy+len+5,sx+0,"Draw!!");
|
|
else
|
|
mvprintw(sy+len+5,sx+0,"Player %d won the game!",(score[1]>score[0]) +1);
|
|
printw(" Wanna play again?(y/n)");
|
|
curs_set(1);
|
|
flushinp();
|
|
input=getch();
|
|
curs_set(0);
|
|
if(input != 'N' && input != 'n' && input!='q')
|
|
goto Start;
|
|
endwin();
|
|
return EXIT_SUCCESS;
|
|
}
|