1
0
mirror of https://github.com/abakh/nbsdgames.git synced 2024-12-04 14:46:22 -05:00

Snakeduel!

This commit is contained in:
Your Name 2021-04-17 01:46:54 +04:30
parent 5efed29a4f
commit 2882f9b0ab
2 changed files with 811 additions and 26 deletions

View File

@ -1,23 +1,29 @@
# -*- Makefile -*-
CFLAGS= -O3 -lncurses -Wno-unused-result
#-O3 --std=c99 -lcurses -DNO_MOUSE for BSD curses
#adding --std=c99 makes warnings in GNU, and the blame is upon glibc feature test macros. my code is correct.
all: jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt
ifndef $(CFLAGS)
CFLAGS= -O3 -lncurses -Wno-unused-result
#-O3 --std=c99 -lcurses -DNO_MOUSE for NetBSD curses
#adding --std=c99 makes warnings in GNU, and the blame is upon glibc feature test macros. my code is correct.
endif
ifndef $(GAMES_DIR)
GAMES_DIR=/usr/games
endif
ifndef $(SCORES_DIR)
SCORES_DIR=/usr/games
endif
all: jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt snakeduel
scorefiles:
touch /usr/games/pp_scores
touch /usr/games/jw_scores
touch /usr/games/mt_scores
touch /usr/games/mnch_scores
touch /usr/games/fsh_scores
touch /usr/games/drt_scores
chmod 666 /usr/games/pp_scores
chmod 666 /usr/games/jw_scores
chmod 666 /usr/games/mt_scores
chmod 666 /usr/games/mnch_scores
chmod 666 /usr/games/fsh_scores
chmod 666 /usr/games/drt_scores
touch $(SCORES_DIR)/pp_scores
touch $(SCORES_DIR)/jw_scores
touch $(SCORES_DIR)/mt_scores
touch $(SCORES_DIR)/mnch_scores
touch $(SCORES_DIR)/fsh_scores
touch $(SCORES_DIR)/drt_scores
chmod 666 $(SCORES_DIR)/pp_scores
chmod 666 $(SCORES_DIR)/jw_scores
chmod 666 $(SCORES_DIR)/mt_scores
chmod 666 $(SCORES_DIR)/mnch_scores
chmod 666 $(SCORES_DIR)/fsh_scores
chmod 666 $(SCORES_DIR)/drt_scores
jewels: jewels.c config.h
$(CC) jewels.c $(CFLAGS) -o ./jewels
@ -51,12 +57,13 @@ redsquare: redsquare.c config.h
$(CC) redsquare.c $(CFLAGS) -o ./redsquare
darrt: darrt.c config.h
$(CC) darrt.c $(CFLAGS) -lm -o ./darrt
clean:
rm ./jewels ./sudoku ./checkers ./mines ./reversi ./battleship ./rabbithole ./sos ./pipes ./fifteen ./memoblocks ./fisher ./muncher ./miketron ./redsquare ./darrt
uninstall:
rm $(PREFIX)/jewels $(PREFIX)/sudoku $(PREFIX)/checkers $(PREFIX)/mines $(PREFIX)/reversi $(PREFIX)/battleship $(PREFIX)/rabbithole $(PREFIX)/sos $(PREFIX)/pipes $(PREFIX)/fifteen $(PREFIX)/memoblocks $(PREFIX)/fisher $(PREFIX)/muncher $(PREFIX)/miketron $(PREFIX)/redsquare $(PREFIX)/darrt
copy_sources:
cp Makefile config.h jewels.c sudoku.c mines.c reversi.c checkers.c battleship.c rabbithole.c sos.c pipes.c fifteen.c memoblocks.c fisher.c muncher.c miketron.c redsquare.c darrt.c $(PREFIX)
install: scorefiles jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt
cp jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt $(PREFIX)
snakeduel: snakeduel.c config.h
$(CC) snakeduel.c $(CFLAGS) -o ./snakeduel
clean:
rm ./jewels ./sudoku ./checkers ./mines ./reversi ./battleship ./rabbithole ./sos ./pipes ./fifteen ./memoblocks ./fisher ./muncher ./miketron ./redsquare ./darrt ./snakeduel
uninstall:
rm $(GAMES_DIR)/jewels $(GAMES_DIR)/sudoku $(GAMES_DIR)/checkers $(GAMES_DIR)/mines $(GAMES_DIR)/reversi $(GAMES_DIR)/battleship $(GAMES_DIR)/rabbithole $(GAMES_DIR)/sos $(GAMES_DIR)/pipes $(GAMES_DIR)/fifteen $(GAMES_DIR)/memoblocks $(GAMES_DIR)/fisher $(GAMES_DIR)/muncher $(GAMES_DIR)/miketron $(GAMES_DIR)/redsquare $(GAMES_DIR)/darrt $(GAMES_DIR)/snakeduel
install: scorefiles jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt snakeduel
cp jewels sudoku mines reversi checkers battleship rabbithole sos pipes fifteen memoblocks fisher muncher miketron redsquare darrt snakeduel $(GAMES_DIR)

778
snakeduel.c Normal file
View File

@ -0,0 +1,778 @@
/*
_ _
(_ | :
_)NAKE |.'UEL
Authored by abakh <abakh@tuta.io>
No rights are reserved and this software comes with no warranties of any kind to the extent permitted by law.
compile with -lm -lncurses
*/
#include <curses.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
#include "config.h"
#define SAVE_TO_NUM 10
#define MINLEN 10
#define MAXLEN 127
#define MINWID 40
#define MAXWID 127
#define LOSE -(MAXWID*MAXLEN)
#define WIN_LIMIT 5
//#define REPORT 0
#ifdef REPORT
#define reportif(x) if(x){fprintf(lol,#x" is true\n");fflush(lol);}
#define reportd(x) if(REPORT){fprintf(lol, #x": %ld\n",(long)x);fflush(lol);}
#define reports(x) if(REPORT){fprintf(lol, "line %d: %s\n",__LINE__,x);fflush(lol);}
#else
#define reportif(x)
#define reportd(x)
#define reports(x)
#endif
enum {UP=0,RIGHT,DOWN,LEFT};
enum {BLOCK=0,SURVIVAL,MIRROR,IMITATE};
typedef signed char byte;
/* The Plan9 compiler can not handle VLAs */
#ifdef NO_VLA
#define len 10
#define wid 40
#ifdef Plan9
int usleep(long usec) {
int second = usec/1000000;
long nano = usec*1000 - second*1000000;
struct timespec sleepy = {0};
sleepy.tv_sec = second;
sleepy.tv_nsec = nano;
nanosleep(&sleepy, (struct timespec *) NULL);
return 0;
}
#endif
#else
int len,wid;
#endif//NO_VLA
typedef struct snake{
int y;
int x;
byte direction;
byte fp;
byte strategy;
byte score;
chtype color;
} snake;
snake p;//player
snake c;//computer
byte pscore;
byte cscore;
chtype colors[6]={0};
byte constant_change={0};
bool must_win=0;
FILE* lol;
void logo(void){
mvaddstr(0,0," _ _");
mvaddstr(1,0,"(_ | : ");
mvaddstr(2,0," _)NAKE |.'UEL");
}
void rectangle(void){
for(int y=0;y<=len;++y){
mvaddch(3+y,0,ACS_VLINE);
mvaddch(4+y,1+wid,ACS_VLINE);
}
for(int x=0;x<=wid;++x){
mvaddch(3,x,ACS_HLINE);
mvaddch(4+len,x,ACS_HLINE);
}
mvaddch(3,0,ACS_ULCORNER);
mvaddch(4+len,0,ACS_LLCORNER);
mvaddch(3,1+wid,ACS_URCORNER);
mvaddch(4+len,1+wid,ACS_LRCORNER);
}
void swap(byte* a,byte* b){
byte s= *a;
*a=*b;
*b=s;
}
void swap_long(long* a,long* b){
long s= *a;
*a=*b;
*b=s;
}
byte opposite(byte direction){
switch(direction){
case UP:
return DOWN;
case DOWN:
return UP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
default:
abort();
}
}
snake fake_move(snake s){
switch(s.direction){
case UP:
s.y=s.y-1;
break;
case DOWN:
s.y=s.y+1;
break;
case LEFT:
s.x=s.x-1;
break;
case RIGHT:
s.x=s.x+1;
break;
}
return s;
}
bool blocked(byte board[len][wid],snake s){
s=fake_move(s);
return ( s.y<0 || s.y >=len || s.x<0 || s.x>=wid || board[s.y][s.x] );
}
bool better_change_way(byte board[len][wid],snake s){
if(blocked(board,s)){
return 1;
}
s=fake_move(s);
if(blocked(board,s)){
return 1;
}
return 0;
}
void putfp(byte board[len][wid],snake s){
if(s.x>=0 && s.y>=0 && s.x<wid && s.y<len){
board[s.y][s.x]=s.fp+opposite(s.direction);//putting direction for wiping
}
}
void move_snake(byte board[len][wid],snake *s){
assert(!blocked(board,*s));
*s=fake_move(*s);
putfp(board,*s);
}
void purs(snake me,int y,int x,byte directions[4]){
if(me.y<y){
directions[0]=DOWN;
directions[3]=UP;
}
else{
directions[0]=UP;
directions[3]=DOWN;
}
if(me.x<x){
directions[1]=RIGHT;
directions[2]=LEFT;
}
else{
directions[1]=LEFT;
directions[2]=RIGHT;
}
int x_dist=abs(x-me.x);
int y_dist=abs(y-me.y);
if(x_dist>y_dist){
swap(&directions[0],&directions[1]);
}
if(x_dist==y_dist && x_dist<3 && directions[0]==me.direction){
swap(&directions[0],&directions[1]);
}
}
void avoid(snake me,int y, int x, byte directions[4]){
purs(me,y,x,directions);
for(byte i=0;i<4;++i){
directions[i]=opposite(directions[i]);
}
}
void shuffle(byte directions[4]){
byte a=rand()%4;
byte b=rand()%4;
swap(&directions[a],&directions[b]);
}
void enemy_avoid(snake me,snake enemy,byte directions[4]){
avoid(me,enemy.y,enemy.x,directions);
}
void enemy_pursue(snake me,snake enemy,byte directions[4]){
purs(me,enemy.y,enemy.x,directions);
}
void enemy_block(byte board[len][wid],snake me, snake enemy,byte directions[4]){
snake ahead=enemy;
switch(enemy.direction){
case UP:
if(me.y>enemy.y)//me is to the down of the enemy, so cannot plan to block it's way in advance
goto JustPursue;
break;
case DOWN:
if(me.y<enemy.y)
goto JustPursue;
break;
case RIGHT:
if(me.x<enemy.x)
goto JustPursue;
break;
case LEFT:
if(me.x>enemy.x)
goto JustPursue;
break;
default:
abort();
}
for(byte i=0;i<10;++i){
if(blocked(board,ahead)||ahead.y==me.y||ahead.x==me.x){
purs(me,ahead.y,ahead.x,directions);
return;
}
ahead=fake_move(ahead);
}
JustPursue:
purs(me,ahead.y,ahead.x,directions);
}
void enemy_mirror(snake me,snake enemy,byte directions[4]){
int y,x;
y=len-1-enemy.y;
x=wid-1-enemy.x;
purs(me,y,x,directions);
}
void enemy_block_mirror(snake me,snake enemy,byte directions[4]){
int y_dist=abs(me.y-enemy.y);
int x_dist=abs(me.x-enemy.x);
if(y_dist>x_dist){
purs(me,len-1-enemy.y,enemy.x,directions);
}
else{
purs(me,enemy.y,wid-1-enemy.x,directions);
}
}
void move_to_top(byte array[4],byte index){
byte newtop=array[index];
for(byte i=index;i>0;--i){
array[i]=array[i-1];
}
array[0]=newtop;
}
void leave_escapes(byte board[len][wid],snake me,byte directions[4]){
byte s=3;
for(byte i=0;i<4;i++){
me.direction=directions[s];
if(!better_change_way(board,me)){
move_to_top(directions,s);
}
else{
--s;
}
}
}
long go_deep(byte board[len][wid],snake me,bool randomize){
reports("****go deep***");
if(randomize){
reports("randomize");
}
long m=0;
byte bumps=0;
static byte inc=1;
if(randomize){
inc=-inc;
}
while(!blocked(board,me)){
me=fake_move(me);
++m;
if(m>len+wid){
return m;
}
if(blocked(board,me)||(randomize&&!(rand()%10))){
snake f=me;
byte i;
if(randomize){
f.direction=rand()%4;
}
for(i=0;i<4;++i){
if(f.direction!=opposite(me.direction) || blocked(board,f)){
me=f;
break;
}
else{
f.direction+=4+inc;
f.direction%=4;
}
}
reports("***BUMP!***");
reportd(bumps);
reportd(m);
if(bumps==4){
return m;
}
else{
++bumps;
}
}
}
return m;
}
long mnvrblty(byte board[len][wid],snake me,byte depth){
long m=0;
long max=0,n,max_n;
while(m<=4 && !blocked(board,me)){
me=fake_move(me);
++m;
if(depth){
snake f=me;
max_n=0;
for(byte i=0;i<4;++i){
n=0;
if(i==opposite(me.direction)){
continue;
}
f.direction=i;
for(byte j=0;j<10;++j){
n=go_deep(board,f,j%2);
if(max_n<n){
max_n=n;
}
if(max_n>len+wid){
return max_n;
}
}
reports("Then the maximum became:");
reportd(max_n);
}
if(max<m+max_n){
max=m+max_n;
}
}
}
return max;
}
void sort_directions(long data[4],byte directions[4]){
bool not_sorted=1;
while(not_sorted){
not_sorted=0;
for(byte i=0;i<3;++i){
if(data[i]<data[i+1]){
swap_long(&data[i],&data[i+1]);
swap(&directions[i],&directions[i+1]);
not_sorted=1;
}
}
}
}
void rank_for_survival(byte board[len][wid],snake me,long advantages[4],byte directions[4]){
long max_adv,adv,sum,sum_positives;
for(byte i=0;i<4;++i){
reports("inspecting various directions");
reportd(i);
adv=sum=sum_positives=0;
max_adv=LONG_MIN;
me.direction= directions[i];
adv=mnvrblty(board,me,2);//advantage(board,*me,*enemy,depth-1);
reports("advantage is");
reportd(adv);
if(max_adv<adv){
max_adv=adv;
}
advantages[i]=max_adv;
reportd(advantages[i]);
}
sort_directions(advantages,directions);
reportd(advantages[0]);
reportd(directions[0]);
reportd(advantages[1]);
reportd(directions[1]);
reportd(advantages[2]);
reportd(directions[2]);
reportd(advantages[3]);
reportd(directions[3]);
}
void draw(byte board[len][wid]){
int y,x;
rectangle();
mvprintw(1,16,"Computer's wins: %d",c.score);
mvprintw(2,16,"Your wins: %d",p.score);
for(y=0;y<len;++y){
for(x=0;x<wid;++x){
switch(board[y][x]/4){
case 1:
mvaddch(4+y,x+1,' '|A_STANDOUT|c.color);
break;
case 2:
mvaddch(4+y,x+1,' '|A_STANDOUT|p.color);
break;
}
if(board[y][x]<0)
mvaddch(4+y,x+1,'0'-board[y][x]);
}
}
}
void help(void){
nocbreak();
cbreak();
erase();
logo();
attron(A_BOLD);
mvprintw(3,0," **** THE CONTROLS ****");
attroff(A_BOLD);
mvprintw(4,0,"hjkl/ARROW KEYS : Change direction");
mvprintw(5,0,"q : Quit");
mvprintw(6,0,"F1 & F2: Help on controls & gameplay");
mvprintw(8,0,"Press a key to continue");
refresh();
getch();
erase();
halfdelay(1);
}
void gameplay(void){
nocbreak();
cbreak();
erase();
logo();
attron(A_BOLD);
mvprintw(3,0," **** THE GAMEPLAY ****");
attroff(A_BOLD);
move(4,0);
printw("Don't hit the walls, the other snake and yourself. Kill the other snake.\n");
refresh();
getch();
erase();
halfdelay(1);
}
void sigint_handler(int x){
endwin();
puts("Quit.");
exit(x);
}
long decide(byte board[len][wid],snake *me,snake *enemy){
//do the move that gives the enemy least advantage (or move randomly at depth 0)
//return 0 if you fail to find a way out
snake f=*me;//f:future
static long turn=0;
reports(" **MOVE***********");
reportd(turn);
++turn;
reportd(me->direction);
int y_dist=(abs(me->y-enemy->y));
int x_dist=(abs(me->x-enemy->x));
int dist=(y_dist+x_dist);
long g=go_deep(board,*me,1);
reportd(g);
long max_survival;
byte directions[4]={0,1,2,3};
long advantages[4]={0};
if(me->strategy==IMITATE ){
if(abs(me->y-(len-1-enemy->y))+abs(me->x-(wid-1-enemy->x))>3){
me->strategy=SURVIVAL;
}
else{
me->strategy=IMITATE;
}
}
else if(g<20){
me->strategy=SURVIVAL;
}
else if( dist<20){
me->strategy=BLOCK;
}
else{
me->strategy=MIRROR;
}
bool change_path=0;
if(better_change_way(board,*me)){
change_path=1;
}
else if(me->strategy==IMITATE){
change_path=1;
}
else if(me->strategy==SURVIVAL){
reports("SURVIVAL!@#");
change_path=1;
}
else if(me->strategy==MIRROR){
change_path=better_change_way(board,*me) || ((me->x%2)&&(me->y%3==2)) || ((me->x%2==0)&&(me->y%3==0));
if(better_change_way(board,*me) && !change_path){
reports("fuck you");
}
}
else if(me->strategy==BLOCK){
reports("BLOCK!@#");
change_path= !(rand()%(dist+1)) || !(rand()%(x_dist+1)) || !(rand()%(y_dist+1));//this one wants to leave escapes
if(!change_path && dist<40 && !(rand()%(dist/2+1))){//this one wants to kill
change_path=1;
}
}
if(change_path){
if(me->strategy==IMITATE){
enemy_mirror(*me,*enemy,directions);
}
if(me->strategy==MIRROR){
enemy_mirror(*me,*enemy,directions);
//shuffle(directions);
leave_escapes(board,*me,directions);
reports("did the leave escapes shit");
reports("MIRROR");
}
else if(me->strategy==BLOCK){
if(dist<7){
enemy_pursue(*me,*enemy,directions);
}
/*else if(dist<20){
enemy_block(board,*me,*enemy,directions);
}*/
else{
enemy_block(board,*me,*enemy,directions);
}
leave_escapes(board,*me,directions);
reports("BLOCK");
}
else if(me->strategy==SURVIVAL){
rank_for_survival(board,*me,advantages,directions);
reports("SURVIVAL and I am acting upon it");
}
for(byte i=0;i<4;++i){//if one way is blocked, go for others
reportd(directions[i]);
f.direction=directions[i];
if(!blocked(board,f)){
if(better_change_way(board,f)){
reports("YET THIS MOTHER FUCKER CHOSE:");
reportd(i);
}
*me=f;
move_snake(board,me);
return 1;
}
else{
reports("this fucker didn't choose:");
reportd(directions[i]);
reports("because that way was supposedly blocked.");
}
}
return LOSE;
}
reports("went on");
move_snake(board,me);
return 1;
}
void init_game(byte board[len][wid]){
if(p.score>c.score+2 && rand()%2){
must_win=1;
}
if(must_win && p.score>c.score){
c.strategy=IMITATE;
}
else{
c.strategy=MIRROR;
}
c.direction=0;
c.y=len/2;
c.x=wid*9/20;
c.fp=4;
c.color=colors[rand()%6];
p.direction=0;
p.y=len/2;
p.x=wid*11/20;
p.fp=8;
do{
p.color=colors[rand()%6];
}while(p.color==c.color);
for(byte y=0;y<len;++y){
for(byte x=0;x<wid;++x){
board[y][x]=0;
}
}
}
int main(int argc, char** argv){
#ifdef REPORT
lol=fopen("lol","w");
fflush(lol);
#endif
bool autoset=0;
signal(SIGINT,sigint_handler);
#ifndef NO_VLA
if(argc>3 || (argc==2 && !strcmp("help",argv[1])) ){
printf("Usage: %s [len wid]\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<MINLEN || wid<MINWID || len>500 || wid>500){
puts("At least one of your given dimensions is either too small or too big.");
return EXIT_FAILURE;
}
}
else{
autoset=1;
}
#endif
initscr();
#ifndef NO_VLA
if(autoset){
len=LINES-7;
if(len<MINLEN)
len=MINLEN;
else if(len>MAXLEN)
len=MAXLEN;
wid=COLS-5;
if(wid<MINWID)
wid=MINWID;
else if(wid>MAXWID)
wid=MAXWID;
}
#endif
srand(time(NULL)%UINT_MAX);
byte board[len][wid];
byte win_limit=WIN_LIMIT;
reportd(len);
reportd(wid);
noecho();
cbreak();
keypad(stdscr,1);
if(has_colors()){
start_color();
use_default_colors();
init_pair(1,COLOR_RED,-1);
init_pair(2,COLOR_YELLOW,-1);
init_pair(3,COLOR_GREEN,-1);
init_pair(4,COLOR_CYAN,-1);
init_pair(5,COLOR_MAGENTA,-1);
init_pair(6,COLOR_BLUE,-1);
for(byte b= 0;b<6;++b){
colors[b]=COLOR_PAIR(b+1);
}
colors[1]|=A_BOLD;
}
Start:
if(c.score==win_limit || p.score==win_limit){
win_limit=WIN_LIMIT;
c.score=p.score=0;
must_win=0;
}
if(c.score==p.score && p.score==win_limit-1){
++win_limit;
}
curs_set(0);
halfdelay(1);
init_game(board);
erase();
int preinput=0,input=0;
while(1){
logo();
draw(board);
refresh();
preinput=input;
input = getch();
if( input == KEY_F(1) || input=='?' )
help();
if( input==KEY_F(2) )
gameplay();
if( (input=='k' || input==KEY_UP) && p.y>0 && p.direction != DOWN ){
p.direction=UP;
}
if( (input=='j' || input==KEY_DOWN) && p.y<len-1 && p.direction != UP ){
p.direction=DOWN;
}
if( (input=='h' || input==KEY_LEFT) && p.x>0 && p.direction != RIGHT){
p.direction=LEFT;
}
if( (input=='l' || input==KEY_RIGHT) && p.x<wid-1 && p.direction != LEFT){
p.direction=RIGHT;
}
if( input=='q')
sigint_handler(0);
if( input=='p'){
nocbreak();
cbreak();
erase();
logo();
attron(A_BOLD);
mvaddstr(1,13,"PAUSED");
attroff(A_BOLD);
getch();
halfdelay(1);
}
if(input!=ERR){
if(preinput==input){//if it wasn't there, hitting two keys in less than 0.1 sec would not work
usleep(100000);
flushinp();
}
}
for(byte i=0;i<2;++i){
if(blocked(board,p)){
++c.score;
reports("player died");
goto Die;
}
else{
move_snake(board,&p);
}
/*if(decide(board,&p,&c) == LOSE){//move, if failed die.
++c.score;
reports("computer died");
goto Die;
}*/
if(decide(board,&c,&p) == LOSE){//move, if failed die.
++p.score;
reports("computer died");
goto Die;
}
}
refresh();
}
Die:
nocbreak();
cbreak();
draw(board);
refresh();
mvprintw(5+len,0,"Game over! Wanna play again?(y/n)");
curs_set(1);
input=getch();
if( input!= 'N' && input!= 'n' && input!='q')
goto Start;
endwin();
return EXIT_SUCCESS;
}