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

555 lines
12 KiB
C
Raw Normal View History

2020-06-20 12:26:54 -04:00
/*
_
|_)
| IPES
Authored by abakh <abakh@tuta.io>
2021-04-23 09:39:03 -04:00
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
2020-06-20 12:26:54 -04:00
*/
2019-03-20 10:16:33 -04:00
#include <curses.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <time.h>
#include <signal.h>
#include "config.h"
#define UP 1
#define RIGHT 2
#define DOWN 4
#define LEFT 8
#define CROSSOVER 15
#define FILLED 16
#define FLOWDELAY 5
#define DELAY 3
#define SAVE_TO_NUM 10
#define SY 0
#define SX 7
2020-06-20 12:26:54 -04:00
typedef signed char byte;
typedef unsigned char bitbox;
/* The Plan9 compiler can not handle VLAs */
2020-09-12 06:38:45 -04:00
#ifdef NO_VLA
#define wid 20
#define len 14
2020-06-20 12:26:54 -04:00
#else
int len,wid;
#endif
int py,px,fy,fx;//p: pointer f: fluid
2019-03-20 10:16:33 -04:00
bitbox tocome[5]={0};//the row of pipes in the left side
chtype green=A_BOLD;//will use bold font instead of green if colors are not available
long score;
FILE* scorefile;
char error [150]={0};
void logo(void){
2020-05-18 14:59:29 -04:00
mvprintw(0,0," _ ");
mvprintw(1,0,"|_)");
mvprintw(2,0,"| IPES");
2019-03-20 10:16:33 -04:00
}
byte scorewrite(void){// only saves the top 10, returns the place in the chart
2020-05-18 14:59:29 -04:00
bool deforno;
if( !getenv("PP_SCORES") && (scorefile= fopen(PP_SCORES,"r")) ){
deforno=1;
}
else{
deforno=0;
if( !(scorefile = fopen(getenv("PP_SCORES"),"r")) ){
2019-03-31 15:38:51 -04:00
sprintf(error,"No accessible score files found. You can make an empty text file in %s or set PP_SCORES to such a file to solve this.",PP_SCORES);
2019-03-20 10:16:33 -04:00
return -1;
2020-05-18 14:59:29 -04:00
}
}
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
char namebuff[SAVE_TO_NUM][60];
long scorebuff[SAVE_TO_NUM];
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
memset(namebuff,0,SAVE_TO_NUM*60*sizeof(char) );
memset(scorebuff,0,SAVE_TO_NUM*sizeof(long) );
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
long fuckingscore =0;
char fuckingname[60]={0};
byte location=0;
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
while( fscanf(scorefile,"%59s : %ld\n",fuckingname,&fuckingscore) == 2 && location<SAVE_TO_NUM ){
strcpy(namebuff[location],fuckingname);
scorebuff[location] = fuckingscore;
2020-06-20 12:26:54 -04:00
++location;
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
memset(fuckingname,0,60);
fuckingscore=0;
}
if(deforno)
scorefile = fopen(PP_SCORES,"w+");//get rid of the previous text first
else
scorefile = fopen(getenv("PP_SCORES"), "w+") ;
if(!scorefile){
strcpy(error, "The file cannot be opened in w+. ");
2019-03-20 10:16:33 -04:00
return -1;
}
2020-05-18 14:59:29 -04:00
byte itreached=location;
byte ret = -1;
bool wroteit=0;
2019-03-20 10:16:33 -04:00
2020-06-20 12:26:54 -04:00
for(location=0;location<=itreached && location<SAVE_TO_NUM-wroteit;++location){
2020-05-18 14:59:29 -04:00
if(!wroteit && (location>=itreached || score>=scorebuff[location]) ){
fprintf(scorefile,"%s : %ld\n",getenv("USER"),score);
ret=location;
wroteit=1;
}
if(location<SAVE_TO_NUM-wroteit && location<itreached)
fprintf(scorefile,"%s : %ld\n",namebuff[location],scorebuff[location]);
}
fflush(scorefile);
return ret;
2019-03-20 10:16:33 -04:00
}
void showscores(byte playerrank){
erase();
logo();
if(*error){
mvaddstr(SY,SX,error);
mvprintw(SY+1,SX,"However, your score is %ld.",score);
refresh();
return;
}
2020-05-18 14:59:29 -04:00
if(playerrank == 0){
char formername[60]={0};
long formerscore=0;
rewind(scorefile);
fscanf(scorefile,"%*s : %*d");
if ( fscanf(scorefile,"%s : %ld",formername,&formerscore)==2 && formerscore>0){
2019-03-20 10:16:33 -04:00
byte a = (len-9)/2;
attron(A_BOLD);
2020-06-21 03:02:36 -04:00
mvprintw(SY,SX, "**** ***");
2019-03-20 10:16:33 -04:00
mvprintw(SY+len+1,SX,"***********************");
attroff(A_BOLD);
attron(green);
2020-05-18 14:59:29 -04:00
mvprintw(SY,SX+4,"CONGRATULATIONS!");
attroff(green);
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
mvprintw(SY+a+1,SX," _____ You bet the");
mvprintw(SY+a+2,SX," .' | previous");
mvprintw(SY+a+3,SX," .' | record");
2020-06-21 03:02:36 -04:00
mvprintw(SY+a+4,SX," | .| | of");
2020-05-18 14:59:29 -04:00
mvprintw(SY+a+5,SX," |.' | |%11ld",formerscore);
mvprintw(SY+a+6,SX," | | held by");
mvprintw(SY+a+7,SX," ___| |___%7s!",formername);
2020-06-21 03:02:36 -04:00
mvprintw(SY+a+8,SX," | |");
2020-05-18 14:59:29 -04:00
mvprintw(SY+a+9,SX," |____________|");
2019-03-20 10:16:33 -04:00
mvprintw(len+2,0,"Game over! Press a key to proceed:");
refresh();
getch();
erase();
logo();
2020-05-18 14:59:29 -04:00
}
2019-03-20 10:16:33 -04:00
2020-05-18 14:59:29 -04:00
}
2019-03-20 10:16:33 -04:00
attron(A_BOLD);
mvprintw(3,0," HIGH");
mvprintw(4,0,"SCORES");
attroff(A_BOLD);
2020-05-18 14:59:29 -04:00
//scorefile is still open with w+
char pname[60] = {0};
long pscore=0;
byte rank=0;
rewind(scorefile);
while( rank<SAVE_TO_NUM && fscanf(scorefile,"%s : %ld\n",pname,&pscore) == 2){
2019-03-20 10:16:33 -04:00
move(SY+1+rank,SX+1);
attron(green);
2020-05-18 14:59:29 -04:00
if(rank == playerrank)
printw(">>>");
printw("%d",rank+1);
2019-03-20 10:16:33 -04:00
attroff(green);
printw(") %s : %ld",pname,pscore);
2020-06-20 12:26:54 -04:00
++rank;
2020-05-18 14:59:29 -04:00
}
2019-03-20 10:16:33 -04:00
refresh();
}
//move in direction
void MID(bitbox direction){
switch(direction){
case UP:
2020-06-20 12:26:54 -04:00
--fy;
2019-03-20 10:16:33 -04:00
break;
case DOWN:
2020-06-20 12:26:54 -04:00
++fy;
2019-03-20 10:16:33 -04:00
break;
case LEFT:
2020-06-20 12:26:54 -04:00
--fx;
2019-03-20 10:16:33 -04:00
break;
case RIGHT:
2020-06-20 12:26:54 -04:00
++fx;
2019-03-20 10:16:33 -04:00
break;
}
}
bitbox opposite(bitbox direction){
switch(direction){
case UP:
return DOWN;
case DOWN:
return UP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
}
return 0;
}
void rectangle(void){
2020-06-20 12:26:54 -04:00
for(int y=0;y<=len;++y){
2019-03-20 10:16:33 -04:00
mvaddch(SY+y,SX,ACS_VLINE);
mvaddch(SY+y,SX+wid+1,ACS_VLINE);
}
2020-06-20 12:26:54 -04:00
for(int x=0;x<=wid;++x){
2019-03-20 10:16:33 -04:00
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+1,ACS_URCORNER);
mvaddch(SY+len+1,SX+wid+1,ACS_LRCORNER);
}
//this generates the pipes...
bitbox pipegen(void){
2019-07-30 14:13:10 -04:00
if(rand()%17){//17 so all forms have the same chance
byte a=rand()%4;
2019-03-20 10:16:33 -04:00
byte b;
do{
2019-07-30 14:13:10 -04:00
b=rand()%4;
2019-03-20 10:16:33 -04:00
}while(b==a);
return (1 << a) | ( 1 << b);
}
else
2019-07-30 14:13:10 -04:00
return CROSSOVER;//could not be generated like that
2019-03-20 10:16:33 -04:00
}
//.. and this is only for display
void addpipe(int y,int x,bitbox pipe , bool highlight){
bitbox p= pipe & ~FILLED;
chtype foo ;
switch(p){
case UP|RIGHT :
foo= ACS_LLCORNER;
break;
case UP|DOWN :
foo=ACS_VLINE;
break;
case UP|LEFT :
foo=ACS_LRCORNER;
break;
case DOWN|RIGHT :
foo =ACS_ULCORNER;
break;
case DOWN|LEFT :
foo=ACS_URCORNER;
break;
case LEFT|RIGHT:
foo=ACS_HLINE;
break;
case RIGHT:
foo = '>';
break;
case LEFT:
foo = '<';
break;
case UP:
foo = '^';
break;
case DOWN:
foo = 'v';
break;
case CROSSOVER: //all
foo = ACS_PLUS;
break;
default:
foo = ' ';
break;
}
if( pipe & FILLED )
foo |= green;
mvaddch(y,x, foo|(highlight*A_REVERSE) );
}
//display
void draw(bitbox board[len][wid]){
int y,x;
2020-06-20 12:26:54 -04:00
for(y=0;y<len;++y){
for(x=0;x<wid;++x){
2019-03-20 10:16:33 -04:00
addpipe(SY+1+y,SX+x+1,board[y][x], (y==py&&x==px) );//its highlighted when y==py and x==px
}
}
rectangle();
}
void mouseinput(void){
2020-09-12 06:38:45 -04:00
#ifndef NO_MOUSE
2020-05-18 14:59:29 -04:00
MEVENT minput;
2019-07-30 15:50:57 -04:00
#ifdef PDCURSES
nc_getmouse(&minput);
#else
getmouse(&minput);
#endif
2020-05-18 14:59:29 -04:00
if( minput.y-4 <len && minput.x-1<wid*2){
py=minput.y-(1+SY);
px=minput.x-(1+SX);
}
else
return;
if(minput.bstate & BUTTON1_CLICKED)
ungetch('\n');
2020-09-12 06:38:45 -04:00
#endif //NO_MOUSE
2019-03-20 10:16:33 -04:00
}
//peacefully close when ^C is pressed
void sigint_handler(int x){
endwin();
puts("Quit.");
exit(x);
}
void help(void){
erase();
2020-05-18 14:59:29 -04:00
logo();
attron(A_BOLD);
2020-06-21 03:02:36 -04:00
mvprintw(SY,SX+5,"-* *-");
2020-05-18 14:59:29 -04:00
mvprintw(3,0," HELP");
2019-03-20 10:16:33 -04:00
mvprintw(4,0," PAGE");
mvprintw(SY+7,SX,"YOU CAN ALSO USE THE MOUSE!");
attroff(A_BOLD);
attron(green);
mvprintw(SY,SX+7,"THE CONTROLS");
attroff(green);
2020-05-18 14:59:29 -04:00
mvprintw(SY+1,SX,"RETURN/ENTER : Place/Replace a pipe");
mvprintw(SY+2,SX,"hjkl/ARROW KEYS : Move cursor");
2019-03-20 10:16:33 -04:00
mvprintw(SY+3,SX,"p : Pause");
2020-05-18 14:59:29 -04:00
mvprintw(SY+4,SX,"q : Quit");
2019-03-20 10:16:33 -04:00
mvprintw(SY+5,SX,"f : Toggle fast flow");
mvprintw(SY+6,SX,"g : Go! (End the countdown.)");
2020-05-18 14:59:29 -04:00
mvprintw(SY+6,SX,"F1 & F2 : Help on controls & gameplay");
mvprintw(SY+9,SX,"Press a key to continue");
refresh();
while(getch()==ERR);
erase();
2019-03-20 10:16:33 -04:00
}
void gameplay(void){
2020-05-18 14:59:29 -04:00
erase();
logo();
attron(A_BOLD);
2020-06-21 03:02:36 -04:00
mvprintw(SY,SX+5,"-* *-");
2020-05-18 14:59:29 -04:00
mvprintw(3,0," HELP");
2019-03-20 10:16:33 -04:00
mvprintw(4,0," PAGE");
2020-05-18 14:59:29 -04:00
attroff(A_BOLD);
attron(green);
mvprintw(SY,SX+7,"THE GAMEPLAY");
attroff(green);
mvprintw(SY+1,SX,"Keep maintaining the pipeline and");
mvprintw(SY+2,SX,"don't let the sewage leak.");
refresh();
while(getch()==ERR);
erase();
2019-03-20 10:16:33 -04:00
}
int main(int argc, char** argv){
signal(SIGINT,sigint_handler);
2020-09-12 06:38:45 -04:00
#ifndef NO_VLA
2019-03-20 10:16:33 -04:00
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<5 || wid<5 || len>1000 || wid>1000){
puts("At least one of your given dimensions is too small or too big.");
return EXIT_FAILURE;
}
}
else{
wid=20;
len=14;
}
2020-06-20 12:26:54 -04:00
#endif
2019-03-20 10:16:33 -04:00
initscr();
2020-09-12 06:38:45 -04:00
#ifndef NO_MOUSE
2019-03-20 10:16:33 -04:00
mousemask(ALL_MOUSE_EVENTS,NULL);
2020-09-12 06:38:45 -04:00
#endif
2019-03-20 10:16:33 -04:00
time_t tstart , now, lasttime, giventime=len*wid/4;
2019-07-30 14:13:10 -04:00
srand(time(NULL)%UINT_MAX);
2019-03-20 10:16:33 -04:00
bitbox direction,board[len][wid];
int input;
byte foo;
bool flow,fast;
2020-06-20 12:26:54 -04:00
Start:
2019-03-20 10:16:33 -04:00
flow=0;
fast=0;
score=0;
memset(error,0,150);
memset(board,0,len*wid);
2019-07-30 14:13:10 -04:00
fy=1+(rand()%(len-2) );
fx=1+(rand()%(wid-2) );
board[fy][fx]= 1 << (rand()%4);
2019-03-20 10:16:33 -04:00
direction= board[fy][fx];
board[fy][fx]|=FILLED;
2020-06-20 12:26:54 -04:00
for(foo=0;foo<5;++foo)
2019-03-20 10:16:33 -04:00
tocome[foo]=pipegen();
tstart = time(NULL);
lasttime=0;
initscr();
curs_set(0);
noecho();
cbreak();
halfdelay(DELAY);
keypad(stdscr,1);
if(has_colors()){
start_color();
use_default_colors();
init_pair(2,COLOR_GREEN,-1);
green=COLOR_PAIR(2);
}
py=px=0;
while(1){
now=time(NULL);
erase();
logo();
if(fast){
attron(A_BOLD);
mvprintw(3,0," FAST");
attroff(A_BOLD);
}
if(!flow && giventime >= now-tstart){
mvprintw(4,0,"Time:%ld",giventime-(now-tstart));
mvprintw(5,0,"Score:");
mvprintw(6,0,"%ld",score);
}
else{
mvprintw(4,0,"Score:");
mvprintw(5,0,"%ld",score);
}
2020-06-20 12:26:54 -04:00
for(foo=0;foo<5;++foo)
2019-03-20 10:16:33 -04:00
addpipe(11-foo,4,tocome[foo],0);
draw(board);
refresh();
if(now-tstart == giventime){
flow=1;
}
if(flow && (fast || ( !(now%FLOWDELAY)&& now!=lasttime ) )){
lasttime = now;
MID(direction);
if(fy<len && fx<wid && fy>=0&& fx>=0 && ( board[fy][fx]&opposite(direction) ) ){
if(board[fy][fx] != CROSSOVER && board[fy][fx] != (CROSSOVER|FILLED) )
direction = board[fy][fx] & ~opposite(direction);
2020-06-20 12:26:54 -04:00
++score;
2019-03-20 10:16:33 -04:00
if(fast)
2020-06-20 12:26:54 -04:00
++score;
2019-03-20 10:16:33 -04:00
}
else
goto End;
board[fy][fx]|=FILLED;
}
input = getch();
if( input == KEY_F(1) || input=='?' ){
help();
if(!flow)
2020-05-18 14:59:29 -04:00
tstart += time(NULL)-now;
2019-03-20 10:16:33 -04:00
}
if( input == KEY_F(2) ){
gameplay();
if(!flow)
tstart += time(NULL)-now;
}
if( input == KEY_MOUSE )
mouseinput();
if( (input=='k' || input==KEY_UP) && py>0 )
2020-06-20 12:26:54 -04:00
--py;
2019-03-20 10:16:33 -04:00
if( (input=='j' || input==KEY_DOWN) && py<len-1 )
2020-06-20 12:26:54 -04:00
++py;
2019-03-20 10:16:33 -04:00
if( (input=='h' || input==KEY_LEFT) && px>0 )
2020-06-20 12:26:54 -04:00
--px;
2019-03-20 10:16:33 -04:00
if( (input=='l' || input==KEY_RIGHT) && px<wid-1 )
2020-06-20 12:26:54 -04:00
++px;
if( (input == '\n'||input==KEY_ENTER) && !(board[py][px] & FILLED) ){
2019-03-20 10:16:33 -04:00
if(board[py][px])
score-=3;
board[py][px]=tocome[0];
2020-06-20 12:26:54 -04:00
for(foo=0;foo<4;++foo)
2019-03-20 10:16:33 -04:00
tocome[foo]=tocome[foo+1];
tocome[4]= pipegen();
}
if( input=='f' ){
if(fast){
halfdelay(DELAY);
fast=0;
}
else{
halfdelay(1);
fast=1;
}
}
if( input=='p'){
erase();
logo();
attron(A_BOLD);
mvprintw(3,0,"PAUSED");
attroff(A_BOLD);
refresh();
while(getch()==ERR);
if(!flow)//pausing should not affect the countdown
tstart += time(NULL)-now;//now is not right now
continue;
}
if(!flow && input == 'g' )
flow=1;
if( score < -1000)
goto End;
if( input=='q'){
nocbreak();
cbreak();
curs_set(1);
mvprintw(len+2,0,"Do you want to see the high scores?(y/n)");
input=getch();
if(input == 'N' || input=='n' || input =='q')
sigint_handler(EXIT_SUCCESS);
showscores(scorewrite());
mvprintw(len+2,0,"Press a key to exit:");
refresh();
getch();
sigint_handler(EXIT_SUCCESS);
}
}
End:
nocbreak();
cbreak();
curs_set(1);
attron(A_BOLD|green);
2020-05-18 14:59:29 -04:00
mvprintw(3,0," OOPS!");
attroff(A_BOLD|green);
2019-03-20 10:16:33 -04:00
draw(board);
mvprintw(len+2,0,"Game over! Press a key to see the high scores:");
getch();
showscores(scorewrite());
mvprintw(len+2,0,"Game over!");
printw(" Wanna play again?(y/n)");
input=getch();
if( input!= 'N' && input!= 'n' && input!='q')
goto Start;
endwin();
return EXIT_SUCCESS;
}