mirror of
https://github.com/abakh/nbsdgames.git
synced 2024-12-04 14:46:22 -05:00
Second version
This commit is contained in:
parent
e6e0db8894
commit
e2bfedbee1
62
README.rst
Normal file
62
README.rst
Normal file
@ -0,0 +1,62 @@
|
||||
New BSD Games
|
||||
-------------
|
||||
You have a computing machine from 1980's and you wonder how can you use it?
|
||||
You deal with a GUI-less machine at work and are looking for ways to kill time?
|
||||
You have to make a Reversi AI for your homework and you don't know where to copy it from?
|
||||
You have been so excited about the bsdgames, but have grown tired of playing tetris, snake and robots for billions of times?
|
||||
You feel they have fooled you by bundling stuff like phantasia with a package you except to contain GAMES?
|
||||
|
||||
Don't worry anymore as you've got nbsdgames now!
|
||||
|
||||
I originally made these to be added to NetBSD (but the few i talked with preferred to have games in the repositories rather than in /usr/games itself).
|
||||
|
||||
|
||||
These include:
|
||||
|
||||
Jewels (A game with a gameplay kinda similiar to that of Tetris, NOT my invention)
|
||||
Sudoku
|
||||
Mines (Minesweeper)
|
||||
Reversi
|
||||
Checkers
|
||||
Battleship
|
||||
|
||||
Post Made-for-NetBSD games:
|
||||
|
||||
SOS
|
||||
Rabbithole (A maze-exploring game where you have to gather items from all around the maze rather reaching an end,the idea maybe mine)
|
||||
Pipes (Same as the famous Pipe Mania, unplayable on the environments that don't support the line characters)
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
* make (optional)
|
||||
* A C compiler with C99 enabled
|
||||
* The standard library
|
||||
* libncurses (the dev package if you are on debian-based distros)
|
||||
|
||||
How to run
|
||||
----------
|
||||
|
||||
1) Download the files
|
||||
2) Go to the sources directory
|
||||
3) Set the environment variable PREFIX to the address you want them to be in
|
||||
4) Install
|
||||
|
||||
Like this:
|
||||
|
||||
.. code:: bash
|
||||
cd ~/Downloads/sources
|
||||
export PREFIX= ~/bin
|
||||
make install
|
||||
|
||||
How do these look like
|
||||
-----------------------
|
||||
.. image:: https://raw.githubusercontent.com/untakenstupidnick/new-bsd-games/master/screenshot.png
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
No rights reserved.
|
||||
|
||||
I am living outside the Berne convention and therefore no meaningful licensing can be applied (meaning that it's public domain in most of the world).
|
||||
|
||||
|
BIN
banner.png
Normal file
BIN
banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
linux_exec/battleship
Executable file
BIN
linux_exec/battleship
Executable file
Binary file not shown.
BIN
linux_exec/checkers
Executable file
BIN
linux_exec/checkers
Executable file
Binary file not shown.
BIN
linux_exec/jewels
Executable file
BIN
linux_exec/jewels
Executable file
Binary file not shown.
BIN
linux_exec/mines
Executable file
BIN
linux_exec/mines
Executable file
Binary file not shown.
BIN
linux_exec/pipes
Executable file
BIN
linux_exec/pipes
Executable file
Binary file not shown.
BIN
linux_exec/rabbithole
Executable file
BIN
linux_exec/rabbithole
Executable file
Binary file not shown.
BIN
linux_exec/reversi
Executable file
BIN
linux_exec/reversi
Executable file
Binary file not shown.
BIN
linux_exec/sos
Executable file
BIN
linux_exec/sos
Executable file
Binary file not shown.
BIN
linux_exec/sudoku
Executable file
BIN
linux_exec/sudoku
Executable file
Binary file not shown.
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 14 KiB |
31
sources/Makefile
Normal file
31
sources/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
all: jewels sudoku mines reversi checkers battleship rabbithole sos pipes
|
||||
|
||||
jewels: jewels.c config.h
|
||||
$(CC) jewels.c -lncurses -o ./jewels
|
||||
sudoku: sudoku.c
|
||||
$(CC) sudoku.c -lncurses -lm -o ./sudoku
|
||||
mines: mines.c
|
||||
$(CC) mines.c -lncurses -o ./mines
|
||||
reversi: reversi.c
|
||||
$(CC) reversi.c -lncurses -o ./reversi
|
||||
checkers: checkers.c
|
||||
$(CC) checkers.c -lncurses -o ./checkers
|
||||
battleship: battleship.c
|
||||
$(CC) battleship.c -lncurses -o ./battleship
|
||||
rabbithole: rabbithole.c
|
||||
$(CC) rabbithole.c -lncurses -o ./rabbithole
|
||||
sos: sos.c
|
||||
$(CC) sos.c -lncurses -o ./sos
|
||||
pipes: pipes.c config.h
|
||||
$(CC) pipes.c -lncurses -o ./pipes
|
||||
clean:
|
||||
rm ./jewels ./sudoku ./checkers ./mines ./reversi ./battleship ./rabbithole ./sos ./pipes
|
||||
uninstall:
|
||||
rm $(PREFIX)/jewels $(PREFIX)/sudoku $(PREFIX)/checkers $(PREFIX)/mines $(PREFIX)/reversi $(PREFIX)/battleship $(PREFIX)/rabbithole $(PREFIX)/sos $(PREFIX)/pipes
|
||||
copy_sources:
|
||||
cp Makefile config.h jewels.c sudoku.c mines.c reversi.c checkers.c battleship.c rabbithole.c sos.c pipes.c $(PREFIX)
|
||||
install: all
|
||||
cp jewels sudoku mines reversi checkers battleship rabbithole sos pipes $(PREFIX)
|
||||
|
618
sources/battleship.c
Normal file
618
sources/battleship.c
Normal file
@ -0,0 +1,618 @@
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.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
|
||||
/*
|
||||
_
|
||||
|_)
|
||||
|_)ATTLESHIP
|
||||
|
||||
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;
|
||||
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){
|
||||
MEVENT minput;
|
||||
getmouse(&minput);
|
||||
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');
|
||||
}
|
||||
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 ");
|
||||
}
|
||||
|
||||
void draw(bool side,byte sy,byte sx){//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 )
|
||||
ch |= ACS_BLOCK|colors[side];
|
||||
else if(game[side][y][x]== MISS)
|
||||
ch |= 'O'|colors[CYAN];
|
||||
else
|
||||
ch |= '~'|colors[CYAN];
|
||||
|
||||
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=random()%10;
|
||||
realx=random()%10;
|
||||
invain=0;
|
||||
SetDirection:
|
||||
y=realy;
|
||||
x=realx;
|
||||
direction=random()%4;
|
||||
for(l=0;(type != 6 && l<type) || (type==6 && l<3) ; l++){//there are two kinds of ship sized 3 tiles
|
||||
if( y<0 || x<0 || y>=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);
|
||||
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' )
|
||||
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<type) || (type==6 && l<3) ; l++){//there are two kinds of ship sized 3 tiles
|
||||
if( y<0 || x<0 || y>=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);
|
||||
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'+(random()%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){
|
||||
byte s = game[!turn][y][x];
|
||||
if(s>0){
|
||||
game[!turn][y][x]=HIT;
|
||||
return 1;
|
||||
}
|
||||
else if(s==HIT || s==MISS || y<0 || x<0 || y>9 || x>9 ){ //didn't shoot at all
|
||||
return NOTHING;
|
||||
}
|
||||
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] ){
|
||||
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 the others 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.*/
|
||||
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]<score[!side] && random()%2 ){
|
||||
cheat(side);
|
||||
return;
|
||||
}
|
||||
while(1){
|
||||
y = random()%10;
|
||||
x = random()%10;
|
||||
r = shoot(side,y,x);
|
||||
if(r == 1){
|
||||
firstinrowy=y;
|
||||
firstinrowx=x;
|
||||
}
|
||||
if(r != NOTHING)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if( lastinrowy ==NOTHING ){
|
||||
if(goindirection == NOTHING)
|
||||
goindirection = random()%4;
|
||||
while(1){
|
||||
y= firstinrowy;//we know there is hit already
|
||||
x= firstinrowx;
|
||||
MID(&y,&x,goindirection);
|
||||
r= shoot(side,y,x);
|
||||
if( r != 1 ){
|
||||
goindirection = (goindirection+1)%4;//the ship is oriented in another way then
|
||||
shotinvain++;
|
||||
|
||||
if(shotinvain==4){ // this only occurs in case of a ship being shot before but not sunk ( e.g. in exprimenting for the direction)
|
||||
shotinvain=0;
|
||||
y=firstinrowy;
|
||||
x=firstinrowx;
|
||||
goindirection = (goindirection+1)%4;
|
||||
while(game[!side][y][x]==HIT){//go till you reach an unshot tile
|
||||
MID(&y,&x,goindirection);
|
||||
if( (y<0 || x<0 || y>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();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
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");
|
||||
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=-1;
|
||||
shotinvain=0;
|
||||
sunk[0]=sunk[1]=0;
|
||||
memset(game,SEA,200);
|
||||
srandom(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);
|
||||
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'){
|
||||
byte r=shoot(turn,py,px-10);
|
||||
if(r != -1){
|
||||
goto Turn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
End:
|
||||
erase();
|
||||
header(won);
|
||||
draw(won,3,0);
|
||||
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;
|
||||
}
|
678
sources/checkers.c
Normal file
678
sources/checkers.c
Normal file
@ -0,0 +1,678 @@
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <math.h>
|
||||
#define LIGHT -1
|
||||
#define DARK 1
|
||||
#define KING 2
|
||||
#define DOESNT_MATTER 1
|
||||
#define IMAGINARY 0
|
||||
#define NORMAL 1
|
||||
#define ALT_IMG 2
|
||||
#define ALT_NRM 3
|
||||
#define WIN 100000
|
||||
/*
|
||||
.-.
|
||||
| '
|
||||
'._.HECKERS
|
||||
|
||||
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;
|
||||
byte py,px;//cursor
|
||||
byte cy,cx;//selected(choosen) piece
|
||||
int dpt;
|
||||
byte game[8][8];
|
||||
byte computer[2]={0,0};
|
||||
byte score[2];//set by header()
|
||||
bool endgame=false;
|
||||
byte jumpagainy , jumpagainx;
|
||||
bool kinged;//if a piece jumps over multiple others and becomes a king it cannot continue jumping
|
||||
|
||||
bool in(byte A[4],byte B[4],byte a,byte b){
|
||||
for(byte c=0;c<4;c++)
|
||||
if(A[c]==a && B[c]==b)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
void rectangle(byte sy,byte sx){
|
||||
byte y,x;
|
||||
for(y=0;y<=8+1;y++){
|
||||
mvaddch(sy+y,sx,ACS_VLINE);
|
||||
mvaddch(sy+y,sx+8*2,ACS_VLINE);
|
||||
}
|
||||
for(x=0;x<=8*2;x++){
|
||||
mvaddch(sy,sx+x,ACS_HLINE);
|
||||
mvaddch(sy+8+1,sx+x,ACS_HLINE);
|
||||
}
|
||||
mvaddch(sy,sx,ACS_ULCORNER);
|
||||
mvaddch(sy+8+1,sx,ACS_LLCORNER);
|
||||
mvaddch(sy,sx+8*2,ACS_URCORNER);
|
||||
mvaddch(sy+8+1,sx+8*2,ACS_LRCORNER);
|
||||
}
|
||||
void header(void){
|
||||
score[0]=score[1]=0;
|
||||
byte y,x;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
if(game[y][x]){
|
||||
if(game[y][x]<0)
|
||||
score[0]++;
|
||||
else
|
||||
score[1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mvprintw(0,0," .-.");
|
||||
mvprintw(1,0,"| ' %2d:%2d",score[0],score[1]);
|
||||
mvprintw(2,0,"'._,HECKERS ");
|
||||
}
|
||||
void draw(byte sy,byte sx){//the game's board
|
||||
rectangle(sy,sx);
|
||||
chtype ch ;
|
||||
byte y,x;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
ch=A_NORMAL;
|
||||
if(y==py && x==px)
|
||||
ch |= A_STANDOUT;
|
||||
if(y==cy && x==cx)
|
||||
ch |= A_BOLD;
|
||||
if(game[y][x]){
|
||||
if(game[y][x]<0){
|
||||
if(has_colors())
|
||||
ch|=COLOR_PAIR(1);
|
||||
else
|
||||
ch |= A_UNDERLINE;
|
||||
}
|
||||
if(abs(game[y][x])<2)
|
||||
ch |='O';
|
||||
else
|
||||
ch |='K';
|
||||
}
|
||||
else if( (y%2) != (x%2) )
|
||||
ch|='.';
|
||||
else
|
||||
ch|=' ';
|
||||
mvaddch(sy+1+y,sx+x*2+1,ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
//place the pieces on the board
|
||||
void fill(void){
|
||||
byte y,x;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
game[y][x]=0;
|
||||
if( (y%2) != (x%2)){
|
||||
if(y<3) game[y][x]=1;
|
||||
if(y>4) game[y][x]=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//fill mvy/x with possible moves
|
||||
bool moves(byte ty,byte tx,byte mvy[4],byte mvx[4]){
|
||||
bool ret=0;
|
||||
byte ndx=0;
|
||||
byte t= game[ty][tx];
|
||||
move(15,0);
|
||||
byte dy,dx;
|
||||
for(dy=-1;dy<2;dy++){
|
||||
for(dx=-1;dx<2;dx++){
|
||||
if( !dy || !dx || (!ty && dy<0) || (!tx && dx<0) || (dy==-t) || (ty+dy>=8) || (tx+dx>=8) )
|
||||
;
|
||||
else if(!game[ty+dy][tx+dx]){
|
||||
ret=1;
|
||||
mvy[ndx]=ty+dy;
|
||||
mvx[ndx]=tx+dx;
|
||||
ndx++;
|
||||
}
|
||||
else
|
||||
ndx++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//would be much faster than applying moves() on every tile
|
||||
bool can_move(byte side){
|
||||
byte y , x ,t, dy , dx;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
if( (t=game[y][x])*side > 0 ){
|
||||
for(dy=-1;dy<2;dy++){
|
||||
for(dx=-1;dx<2;dx++){
|
||||
if( !dy || !dx || (!y && dy<0) || (!x && dx<0) || (dy==-t) || (y+dy>=8) || (x+dx>=8) )
|
||||
;
|
||||
else if( !game[y+dy][x+dx] )
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
//fill mvy/x with possible jumping moves
|
||||
bool jumps(byte ty,byte tx,byte mvy[4],byte mvx[4]){
|
||||
bool ret=0;
|
||||
byte ndx=0;
|
||||
byte ey,ex;
|
||||
byte t= game[ty][tx];
|
||||
byte dy,dx;
|
||||
for(dy=-1;dy<2;dy++){
|
||||
for(dx=-1;dx<2;dx++){
|
||||
ey = dy*2;
|
||||
ex = dx*2;
|
||||
if(!dy || !dx ||(dy==-t)|| (ty+ey<0) || (tx+ex<0) || (ty+ey>=8) || (tx+ex>=8) )
|
||||
;
|
||||
else if(!game[ty+ey][tx+ex] && game[ty+dy][tx+dx]*t<0){
|
||||
ret=1;
|
||||
mvy[ndx]=ty+ey;
|
||||
mvx[ndx]=tx+ex;
|
||||
ndx++;
|
||||
}
|
||||
else
|
||||
ndx++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//same as can_move for jumps
|
||||
byte can_jump(byte ty,byte tx){
|
||||
byte dy,dx,t=game[ty][tx];
|
||||
byte ey,ex;
|
||||
byte ret=0;
|
||||
for(dy=-1;dy<2;dy++){
|
||||
for(dx=-1;dx<2;dx++){
|
||||
ey=dy*2;
|
||||
ex=dx*2;
|
||||
if((dy==-t)||(ty+ey<0)||(tx+ex<0)||(ty+ey>=8)||(tx+ex>=8) )
|
||||
;
|
||||
else if(!game[ty+dy*2][tx+dx*2]&&game[ty+dy][tx+dx]*t<0){
|
||||
ret++;
|
||||
if(ret>1)
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//see if the side is forced to do a jump
|
||||
byte forced_jump(byte side){
|
||||
byte y,x;
|
||||
byte foo,ret;
|
||||
foo=ret=0;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
if(game[y][x]*side>0 && (foo=can_jump(y,x)) )
|
||||
ret+=foo;
|
||||
if(ret>1)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
byte cmove(byte fy,byte fx,byte sy,byte sx){//really move/jump , 'move' is a curses function
|
||||
byte a = game[fy][fx];
|
||||
byte ret=0;
|
||||
game[fy][fx]=0;
|
||||
game[sy][sx]=a;
|
||||
if(abs(fy-sy) == 2){
|
||||
ret =game[(fy+sy)/2][(fx+sx)/2];
|
||||
game[(fy+sy)/2][(fx+sx)/2]=0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//make the pawn a king
|
||||
bool king(byte y,byte x){
|
||||
byte t= (4-y)*game[y][x];
|
||||
if( (y==7 || !y) && t<0 && t>-5 ){
|
||||
game[y][x]*=2;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
double advantage(byte side){
|
||||
unsigned char own,opp;
|
||||
own=opp=0;
|
||||
byte foo;
|
||||
byte y,x;
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
foo=game[y][x]*side;
|
||||
if(foo>0){
|
||||
own++;//so it wont sacrfice two pawns for a king ( 2 kings == 3 pawns)
|
||||
own+=foo;
|
||||
}
|
||||
else if(foo<0){
|
||||
opp++;
|
||||
opp-=foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!own)
|
||||
return 0;
|
||||
else if(!opp)
|
||||
return WIN;
|
||||
else
|
||||
return (double)own/opp;
|
||||
}
|
||||
double posadvantage(byte side){
|
||||
double adv=0;
|
||||
double oppadv=0;
|
||||
byte foo;
|
||||
byte y,x;
|
||||
byte goal= (side>0)*7 , oppgoal=(side<0)*7;
|
||||
/*This encourages the AI to king its pawns and concentrate its kings in the center.
|
||||
The idea is : With forces concentrated in the center, movements to all of the board would be in the game tree's horizon of sight(given enough depth);
|
||||
and with forces being focused , its takes less movements to make an attack. */
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
foo=game[y][x]*side;
|
||||
if(foo>0){
|
||||
adv+=foo;
|
||||
adv++;
|
||||
if(foo==1)
|
||||
adv+= 1/( abs(y-goal) );//adding positional value
|
||||
else if(foo==2)
|
||||
adv+= 1/( fabs(y-3.5)+ fabs(x-3.5) );
|
||||
}
|
||||
else if( foo<0 ){
|
||||
oppadv-=foo;
|
||||
oppadv++;
|
||||
if(foo==-1)
|
||||
adv+=1/( abs(y-oppgoal) );
|
||||
else if(foo==-2)
|
||||
adv+= 1/( fabs(y-3.5)+ fabs(x-3.5) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!adv)
|
||||
return 0;
|
||||
else if( !oppadv )
|
||||
return WIN;
|
||||
else
|
||||
return adv/oppadv;
|
||||
return adv;
|
||||
}
|
||||
//the AI algorithm
|
||||
double decide(byte side,byte depth,byte s){//s is the type of move, it doesn't stand for anything
|
||||
byte fj=forced_jump(side);//only one legal jump if returns 1
|
||||
byte nextturn;
|
||||
|
||||
byte mvy[4],mvx[4];
|
||||
byte n;
|
||||
|
||||
bool didking;
|
||||
byte captured;
|
||||
|
||||
double adv=0;
|
||||
byte toy,tox;
|
||||
byte y,x;
|
||||
|
||||
double wrstadv=WIN+1;
|
||||
|
||||
double bestadv=0;
|
||||
byte besttoy,besttox;
|
||||
byte besty,bestx;
|
||||
bestx=besty=besttox=besttoy=-100;
|
||||
bool canmove=0;
|
||||
|
||||
byte nexts ;
|
||||
if(s == IMAGINARY || s == NORMAL )
|
||||
nexts=IMAGINARY;
|
||||
else
|
||||
nexts=ALT_IMG;
|
||||
|
||||
for(y=0;y<8;y++){
|
||||
for(x=0;x<8;x++){
|
||||
if(fj && (s==NORMAL || s==ALT_NRM) && jumpagainy>=0 && (jumpagainy!=y || jumpagainx!=x) )
|
||||
continue;
|
||||
if(game[y][x]*side>0){
|
||||
canmove=0;
|
||||
memset(mvy,-1,4);
|
||||
memset(mvx,-1,4);
|
||||
if(fj)
|
||||
canmove=jumps(y,x,mvy,mvx);
|
||||
else
|
||||
canmove=moves(y,x,mvy,mvx);
|
||||
if(canmove){
|
||||
for(n=0;n<4;n++){
|
||||
if(mvy[n] != -1){//a real move
|
||||
toy=mvy[n];
|
||||
tox=mvx[n];
|
||||
captured=cmove(y,x,toy,tox);//do the imaginary move
|
||||
if(fj && can_jump(toy,tox) ) //its a double jump
|
||||
nextturn=side;
|
||||
else
|
||||
nextturn=-side;
|
||||
didking=king(toy,tox);
|
||||
|
||||
//see the advantage you get
|
||||
if(fj==1 && (s==ALT_NRM || s==NORMAL) )
|
||||
adv= DOESNT_MATTER;//you have to do the move anyway
|
||||
else if(!depth){
|
||||
if(s==IMAGINARY || s==NORMAL)//calculating advantage only based on numerical superiority
|
||||
adv=advantage(side);
|
||||
else
|
||||
adv=posadvantage(side);//taking to account the position of the pieces
|
||||
}
|
||||
else{
|
||||
if(nextturn==side)
|
||||
adv=decide(nextturn,depth,nexts);
|
||||
else{ //best move is the one that gives least advantage to the opponet
|
||||
adv=decide(nextturn,depth-!fj,nexts);
|
||||
if(adv==WIN)
|
||||
adv=0;
|
||||
else if(adv)
|
||||
adv=1/adv;
|
||||
else
|
||||
adv=WIN;
|
||||
}
|
||||
}
|
||||
//undo the imaginary move
|
||||
if(didking)
|
||||
game[toy][tox]/=2;
|
||||
game[y][x]=game[toy][tox];
|
||||
game[toy][tox]=0;
|
||||
if(fj)
|
||||
game[(toy+y)/2][(tox+x)/2]=captured;
|
||||
|
||||
if(besty<0 || adv>bestadv || (adv==bestadv && ( random()%2 )) ){
|
||||
besty=y;
|
||||
bestx=x;
|
||||
besttoy=toy;
|
||||
besttox=tox;
|
||||
bestadv=adv;
|
||||
}
|
||||
if(adv<wrstadv)
|
||||
wrstadv=adv;
|
||||
if(fj == 1)
|
||||
goto EndLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EndLoop:
|
||||
if( (s==NORMAL || s==ALT_NRM) && besty >= 0 ){
|
||||
if(endgame && fj!=1 && s==NORMAL && bestadv==wrstadv ){//the algorithm is not given enough depth to determine which move is better
|
||||
if(wrstadv == WIN){//the randomization in the algorithm may cause an illusion of an inevitable win in several moves
|
||||
if(depth > 1)
|
||||
decide(side,depth-1,NORMAL);
|
||||
else
|
||||
goto Move;
|
||||
}
|
||||
else
|
||||
decide(side,depth,ALT_NRM);//change your opinion about what advantage means
|
||||
}
|
||||
else{
|
||||
Move:
|
||||
cmove(besty,bestx,besttoy,besttox);
|
||||
kinged=king(besttoy,besttox);
|
||||
if(!kinged && can_jump(besttoy,besttox) ){
|
||||
jumpagainy = besttoy;//so the next player (itself) can only continue the chain of jumps from there
|
||||
jumpagainx = besttox;
|
||||
}
|
||||
else
|
||||
jumpagainy=jumpagainx=-1;
|
||||
}
|
||||
}
|
||||
return bestadv;
|
||||
}
|
||||
//peacefully close when ^C is pressed
|
||||
void sigint_handler(int x){
|
||||
endwin();
|
||||
puts("Quit.");
|
||||
exit(x);
|
||||
}
|
||||
|
||||
void mouseinput(void){
|
||||
MEVENT minput;
|
||||
getmouse(&minput);
|
||||
if( minput.y-4 <8 && minput.x-1<16){
|
||||
py=minput.y-4;
|
||||
px=(minput.x-1)/2;
|
||||
}
|
||||
else
|
||||
return;
|
||||
if(minput.bstate & (BUTTON1_CLICKED|BUTTON1_PRESSED|BUTTON1_RELEASED) )
|
||||
ungetch('\n');
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
header();
|
||||
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 : Select or move the piece");
|
||||
mvprintw(5,0,"SPACE : Flag/Unflag");
|
||||
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");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
header();
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE GAMEPLAY ****");
|
||||
attroff(A_BOLD);
|
||||
move(4,0);
|
||||
printw("1) The game starts with each player having 12 men;\n");
|
||||
printw(" men can only diagonally move forwards \n");
|
||||
printw(" (toward the opponet's side).\n\n");
|
||||
printw("2) Men can become kings by reaching the opponet's \n");
|
||||
printw(" first rank; kings can diagonally move both forwards\n");
|
||||
printw(" and backwards.\n\n");
|
||||
printw("3) Pieces can capture opponet's pieces by jumping over them\n");
|
||||
printw(" also they can capture several pieces at once by doing a\n");
|
||||
printw(" chain of jumps.\n\n");
|
||||
printw("4) You have to do a jump if you can.\n\n");
|
||||
printw("5) A player wins when the opponet can't do a move e. g. \n");
|
||||
printw(" all of their pieces are captured.\n\n");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
int main(int argc,char** argv){
|
||||
dpt=4;
|
||||
if(argc>2){
|
||||
printf("Usage: %s [AIpower]\n",argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(argc==2){
|
||||
if(sscanf(argv[1],"%d",&dpt) && dpt<128 && dpt>0)
|
||||
;
|
||||
else{
|
||||
puts("That should be a number from 1 to 127.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
initscr();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
noecho();
|
||||
cbreak();
|
||||
keypad(stdscr,1);
|
||||
int input ;
|
||||
printw("Dark plays first.\nChoose type of the dark player(H/c)\n" );
|
||||
input=getch();
|
||||
if(input=='c'){
|
||||
computer[0]=dpt;
|
||||
printw("Computer.\n");
|
||||
}
|
||||
else{
|
||||
computer[0]=0;
|
||||
printw("Human.\n");
|
||||
}
|
||||
printw("Choose type of the light 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_RED,-1);
|
||||
}
|
||||
signal(SIGINT,sigint_handler);
|
||||
Start:
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
fill();
|
||||
cy=cx=-1;
|
||||
py=px=0;
|
||||
byte mvy[4],mvx[4];
|
||||
memset(mvy,-1,4);
|
||||
memset(mvx,-1,4);
|
||||
byte turn=1;
|
||||
bool t=1;
|
||||
bool fj;
|
||||
byte result;
|
||||
byte todraw=0;
|
||||
double adv = 1;//used to determine when the game is a draw
|
||||
double previousadv =1;
|
||||
Turn:
|
||||
jumpagainy=jumpagainx=-1;
|
||||
kinged=0;
|
||||
turn =-turn;
|
||||
t=!t;//t == turn<0 that's turn in binary/array index format
|
||||
fj = forced_jump(turn);
|
||||
erase();
|
||||
flushinp();
|
||||
header();
|
||||
draw(3,0);
|
||||
if(t){
|
||||
previousadv=adv;
|
||||
adv= advantage(1) + (score[0]*score[1]);//just taking the dry scores to account too,nothing special
|
||||
if(previousadv==adv)
|
||||
todraw++;
|
||||
else
|
||||
todraw=0;
|
||||
}
|
||||
if(!score[0] || (turn==-1 && !fj && !can_move(-1))){
|
||||
result=1;
|
||||
goto End;
|
||||
}
|
||||
else if(!score[1] || (turn==1 && !fj && !can_move(1))){
|
||||
result=-1;
|
||||
goto End;
|
||||
}
|
||||
else if(todraw==50){ // 50 turns without any gains for each side
|
||||
result=0;
|
||||
goto End;
|
||||
}
|
||||
endgame= score[t]<=5 || score[!t]<=5;
|
||||
draw(3,0);
|
||||
refresh();
|
||||
while(computer[t]){
|
||||
mvprintw(13,0,"Thinking...");
|
||||
refresh();
|
||||
computer[t]=dpt+ (score[!t] != score[t]) + endgame;
|
||||
decide(turn,computer[t],1);
|
||||
if(!(fj && jumpagainy>=0 && !kinged )){
|
||||
goto Turn;
|
||||
}
|
||||
}
|
||||
while(1){
|
||||
erase();
|
||||
draw(3,0);
|
||||
header();
|
||||
refresh();
|
||||
input=getch();
|
||||
if( input == KEY_F(1) || input=='?' )
|
||||
help();
|
||||
if( input == KEY_F(2) )
|
||||
gameplay();
|
||||
if( input == KEY_MOUSE )
|
||||
mouseinput();
|
||||
if( (input=='k' || input==KEY_UP) && py>0)
|
||||
py--;
|
||||
if( (input=='j' || input==KEY_DOWN) && py<7)
|
||||
py++;
|
||||
if( (input=='h' || input==KEY_LEFT) && px>0)
|
||||
px--;
|
||||
if( (input=='l' || input==KEY_RIGHT) && px<7)
|
||||
px++;
|
||||
if( input=='q'){
|
||||
result=2;
|
||||
goto End;
|
||||
}
|
||||
if(input=='\n'){
|
||||
if(game[py][px]*turn>0){
|
||||
cy=py;
|
||||
cx=px;
|
||||
memset(mvy,-1,4);
|
||||
memset(mvx,-1,4);
|
||||
if(!fj)
|
||||
moves(py,px,mvy,mvx);
|
||||
jumps(py,px,mvy,mvx);
|
||||
}
|
||||
if( in(mvy,mvx,py,px) && !(jumpagainy>=0 && (cy !=jumpagainy || cx != jumpagainx) ) ){
|
||||
memset(mvy,-1,4);
|
||||
memset(mvx,-1,4);
|
||||
cmove(cy,cx,py,px);
|
||||
kinged=king(py,px);
|
||||
cy=-1;
|
||||
cx=-1;
|
||||
if( !(fj && can_jump(py,px) && !kinged ) ){
|
||||
goto Turn;
|
||||
}
|
||||
else{
|
||||
jumpagainy=py;
|
||||
jumpagainx=px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
End:
|
||||
move(13,0);
|
||||
switch(result){
|
||||
case -1:
|
||||
printw("The dark side has won the game.");
|
||||
break;
|
||||
case 0:
|
||||
printw("Draw.");
|
||||
break;
|
||||
case 1:
|
||||
printw("The light side has won the game.");
|
||||
break;
|
||||
case 2:
|
||||
printw("You resigned.");
|
||||
}
|
||||
printw(" Wanna rematch?(y/n)");
|
||||
curs_set(1);
|
||||
input=getch();
|
||||
if(result==2){
|
||||
if (input=='Y' || input=='y')
|
||||
goto Start;
|
||||
}
|
||||
else if(input!='n' && input!='N' && input!= 'q'){
|
||||
/*byte b=computer[0]; //switch sides, i don't know if it's necessary
|
||||
computer[0]=computer[1];
|
||||
computer[1]=b;*/
|
||||
goto Start;
|
||||
}
|
||||
endwin();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
3
sources/config.h
Normal file
3
sources/config.h
Normal file
@ -0,0 +1,3 @@
|
||||
#define PP_SCORES "PipesScores"
|
||||
#define JW_SCORES "JewelsScores"
|
||||
//for easier access
|
389
sources/jewels.c
Normal file
389
sources/jewels.c
Normal file
@ -0,0 +1,389 @@
|
||||
#include <curses.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
#define LEN 17
|
||||
#define WID 19
|
||||
#define DELAY 2
|
||||
#define SAVE_TO_NUM 10
|
||||
|
||||
/*
|
||||
Jewels
|
||||
|
||||
|
||||
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 -lnucrses
|
||||
|
||||
A pair of jewels appear on top of the window, And you can move and rotate them while they are falling down.
|
||||
If you make a vertical or horizontal row of 4 jewels they will explode and add up to your score.
|
||||
Like Tetris,You will lose the game when the center of the uppermost row is filled.
|
||||
*/
|
||||
|
||||
typedef signed char byte;
|
||||
chtype board[LEN][WID];
|
||||
chtype colors[6]={0};
|
||||
chtype next1,next2;
|
||||
byte jx,jy; //first jewel's position
|
||||
byte kx,ky;//second jewel's position in relation to that of j
|
||||
long score=0;
|
||||
char* controls = "j,l-Move k-Rotate p-Pause q-Quit";
|
||||
FILE* scorefile;
|
||||
byte scorewrite(long score){// only saves the top 10
|
||||
bool deforno;
|
||||
if( !getenv("JW_SCORES") && (scorefile= fopen(JW_SCORES,"r")) ){
|
||||
deforno=1;
|
||||
}
|
||||
else{
|
||||
deforno=0;
|
||||
if( !(scorefile = fopen(getenv("JW_SCORES"),"r")) ){
|
||||
fprintf(stderr,"\nNo accessible score files found. You can make an empty text file in %s/%s or set JW_SCORES to such a file to solve this. \n",getenv("HOME"),JW_SCORES);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
char namebuff[SAVE_TO_NUM][60];
|
||||
long scorebuff[SAVE_TO_NUM];
|
||||
|
||||
memset(namebuff,0,SAVE_TO_NUM*60*sizeof(char) );
|
||||
memset(scorebuff,0,SAVE_TO_NUM*sizeof(long) );
|
||||
|
||||
long fuckingscore =0;
|
||||
char fuckingname[60]={0};
|
||||
byte location=0;
|
||||
|
||||
while( fscanf(scorefile,"%59s : %ld\n",fuckingname,&fuckingscore) == 2 && location<SAVE_TO_NUM ){
|
||||
strcpy(namebuff[location],fuckingname);
|
||||
scorebuff[location] = fuckingscore;
|
||||
location++;
|
||||
|
||||
memset(fuckingname,0,60);
|
||||
fuckingscore=0;
|
||||
}
|
||||
if(deforno)
|
||||
scorefile = fopen(JW_SCORES,"w+");//get rid of the text
|
||||
else
|
||||
scorefile = fopen(getenv("JW_SCORES"), "w+") ;
|
||||
if(!scorefile){
|
||||
printf("\nThe file cannot be opened in w+.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
byte itreached=location;
|
||||
byte ret = -1;
|
||||
bool wroteit=0;
|
||||
|
||||
for(location=0;location<=itreached && location<SAVE_TO_NUM-wroteit;location++){
|
||||
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;
|
||||
}
|
||||
void showscores(byte playerrank){
|
||||
if(playerrank == 0){
|
||||
char formername[60]={0};
|
||||
long formerscore=0;
|
||||
rewind(scorefile);
|
||||
fscanf(scorefile,"%*s : %*d\n");
|
||||
if ( fscanf(scorefile,"%s : %ld\n",formername,&formerscore)==2){
|
||||
printf("\n*****CONGRATULATIONS!****\n");
|
||||
printf(" _____ You bet the\n");
|
||||
printf(" .' | previous\n");
|
||||
printf(" .' | record\n");
|
||||
printf(" | .| | of\n");
|
||||
printf(" |.' | |%14ld\n",formerscore);
|
||||
printf(" | | held by\n");
|
||||
printf(" ___| |___%11s\n",formername);
|
||||
printf(" | |\n");
|
||||
printf(" |____________|\n");
|
||||
printf("*************************\n");
|
||||
}
|
||||
|
||||
}
|
||||
//scorefile is still open with w+
|
||||
char pname[60] = {0};
|
||||
long pscore=0;
|
||||
byte rank=0;
|
||||
rewind(scorefile);
|
||||
printf("\n>*>*>Top %d<*<*<\n",SAVE_TO_NUM);
|
||||
while( rank<SAVE_TO_NUM && fscanf(scorefile,"%s : %ld\n",pname,&pscore) == 2){
|
||||
if(rank == playerrank)
|
||||
printf(">>>");
|
||||
printf("%d) %s : %ld\n",rank+1,pname,pscore);
|
||||
rank++;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
//apply gravity
|
||||
bool fall(void){
|
||||
bool jfall,kfall,ret;
|
||||
jfall=kfall=ret=0;
|
||||
for(int y=LEN-1;y>0;y--){
|
||||
chtype c,d;
|
||||
for(int x=WID-1;x>=0;x--){
|
||||
c=board[y][x];
|
||||
d=board[y-1][x];
|
||||
if(!c && d){
|
||||
board[y-1][x]=0;
|
||||
board[y][x]=d;
|
||||
if(y-1==jy && x==jx)
|
||||
jfall=1;
|
||||
if((y-1==jy+ky) && (x==jx+kx))
|
||||
kfall=1;
|
||||
ret=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(jfall&&kfall)
|
||||
jy++;
|
||||
else
|
||||
jy = LEN+1;
|
||||
return ret;
|
||||
}
|
||||
// rotate 90d clockwise in ky/x format
|
||||
void clockwise(byte* y,byte* x){
|
||||
/*
|
||||
o x
|
||||
x xo o ox*/
|
||||
chtype fx,fy;
|
||||
if(*y)
|
||||
fy=0;
|
||||
fx=-*y;
|
||||
|
||||
if(*x)
|
||||
fx=0;
|
||||
fy=*x;
|
||||
*y=fy;
|
||||
*x=fx;
|
||||
}
|
||||
|
||||
//rtt jwls
|
||||
bool rotate(void){//f:future
|
||||
if(jy>LEN)
|
||||
return 0;
|
||||
byte fy,fx;
|
||||
fy=ky;fx=kx;
|
||||
clockwise(&fy,&fx);
|
||||
if( jy+fy<0 || jy+fy>=LEN || jx+fx<0 || jx+fx>=WID )
|
||||
return 0;
|
||||
if(board[jy+fy][jx+fx])
|
||||
return 0;
|
||||
chtype a = board[jy+ky][jx+kx];
|
||||
board[jy+ky][jx+kx]=0;
|
||||
ky=fy;
|
||||
kx=fx;
|
||||
board[jy+ky][jx+kx]=a;
|
||||
return 1;
|
||||
}
|
||||
//mv jwls
|
||||
bool jmove(byte dy,byte dx){
|
||||
if(jy>LEN)
|
||||
return 0;
|
||||
|
||||
if(jx+dx>=WID || jx+dx<0 || jx+kx+dx>=WID ||jx+kx+dx<0 || jy+dx<0 ||jx+dx+kx<0)
|
||||
return 0;
|
||||
if( board[jy+ky+dy][jx+kx+dx] )
|
||||
if( !(jy+ky+dy == jy && jx+kx+dx==jx) )
|
||||
return 0;
|
||||
|
||||
if( board[jy+dy][jx+dx])
|
||||
if(!(dx==kx && dy==ky))
|
||||
return 0;
|
||||
//still alive?
|
||||
chtype a = board[jy][jx];
|
||||
chtype b = board[jy+ky][jx+kx];
|
||||
board[jy][jx]=0;
|
||||
board[jy+ky][jx+kx]=0;
|
||||
board[jy+dy][jx+dx]=a;
|
||||
board[jy+ky+dy][jx+kx+dx]=b;
|
||||
jy+=dy;jx+=dx;
|
||||
return 1;
|
||||
}
|
||||
//scoring algorithm
|
||||
bool explode(byte combo){
|
||||
bool ret =0;
|
||||
chtype c,uc;
|
||||
byte n;
|
||||
byte y,x;
|
||||
for(y=0;y<LEN;y++){
|
||||
c=uc=n=0;
|
||||
for(x=0;x<WID;x++){
|
||||
uc = c;
|
||||
c = board[y][x];
|
||||
if(c && c == uc){
|
||||
n++;
|
||||
if(n>=3 && x==WID-1){//the chain ends because the row ends
|
||||
x++;
|
||||
goto HrExplsn;
|
||||
}
|
||||
}
|
||||
else if(n>=3){
|
||||
HrExplsn:
|
||||
score+=n*10*(n-2)*combo;
|
||||
ret=1;
|
||||
for(;n>=0;n--)
|
||||
board[y][x-1-n]=0;
|
||||
n=0;
|
||||
}
|
||||
else
|
||||
n=0;
|
||||
}
|
||||
}
|
||||
for(x=0;x<WID;x++){
|
||||
c=uc=n=0;
|
||||
for(byte y=0;y<LEN;y++){
|
||||
uc=c;
|
||||
c = board[y][x];
|
||||
if(c && c == uc){
|
||||
n++;
|
||||
if(n>=3 && y==LEN-1){
|
||||
y++;
|
||||
goto VrExplsn;
|
||||
}
|
||||
}
|
||||
else if(n>=3){
|
||||
VrExplsn:
|
||||
score+=n*10*(n-2)*combo;
|
||||
ret=1;
|
||||
for(;n>=0;n--)
|
||||
board[y-1-n][x]=0;
|
||||
n=0;
|
||||
}
|
||||
else
|
||||
n=0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//display
|
||||
void draw(void){
|
||||
erase();
|
||||
int middle = (COLS/2-1)-(WID/2);
|
||||
chtype a=A_STANDOUT|' ';
|
||||
mvhline(LEN,middle-2,a,WID+4);
|
||||
mvvline(0,middle-1,a,LEN);
|
||||
mvvline(0,middle-2,a,LEN);
|
||||
mvvline(0,middle+WID,a,LEN);
|
||||
mvvline(0,middle+WID+1,a,LEN);
|
||||
mvprintw(0,0,"Score:%d",score);
|
||||
mvaddstr(1,0,"Next:");
|
||||
addch(next1);
|
||||
addch(next2);
|
||||
for(byte y=0;y<LEN;y++){
|
||||
for(byte x=0;x<WID;x++){
|
||||
chtype c = board[y][x];
|
||||
if(c)
|
||||
mvaddch(y,middle+x,c);
|
||||
}
|
||||
}
|
||||
mvaddstr(LINES-2,middle-5,controls);
|
||||
refresh();
|
||||
}
|
||||
int main(void){
|
||||
initscr();
|
||||
cbreak();
|
||||
halfdelay(DELAY);
|
||||
noecho();
|
||||
curs_set(0);
|
||||
wnoutrefresh(stdscr);
|
||||
keypad(stdscr,1);
|
||||
int input;
|
||||
bool falls;
|
||||
byte stop=0 , combo;
|
||||
char jwstr[] = {'*','^','~','"','$','V'};
|
||||
if(has_colors()){
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1,COLOR_RED,-1);
|
||||
init_pair(2,COLOR_GREEN,-1);
|
||||
init_pair(3,COLOR_MAGENTA,-1);
|
||||
init_pair(4,COLOR_BLUE,-1);//array this thing
|
||||
init_pair(5,COLOR_YELLOW,-1);
|
||||
init_pair(6,COLOR_CYAN,-1);
|
||||
for(byte n=0;n<6;n++){
|
||||
colors[n] = COLOR_PAIR(n+1);
|
||||
}
|
||||
}
|
||||
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
byte ran1= random()%6;
|
||||
byte ran2= random()%6;
|
||||
next1= colors[ran1]|jwstr[ran1];
|
||||
next2= colors[ran2]|jwstr[ran2];
|
||||
while(1){
|
||||
chtype a,b;
|
||||
a=board[0][WID/2];
|
||||
b=board[0][WID/2-1];
|
||||
if(a || b ){
|
||||
goto Lose;
|
||||
}
|
||||
jy=ky=0;
|
||||
jx=WID/2;
|
||||
kx=-1;
|
||||
board[jy][jx]=next2;
|
||||
board[jy+ky][jx+kx]=next1;
|
||||
ran1= random()%6;
|
||||
ran2= random()%6;
|
||||
next1=colors[ran1]|jwstr[ran1];
|
||||
next2=colors[ran2]|jwstr[ran2];
|
||||
falls = 1;
|
||||
while(falls){
|
||||
input = getch();
|
||||
|
||||
if(input != ERR)
|
||||
stop+=1;
|
||||
|
||||
if( stop >= 10){
|
||||
falls=fall();
|
||||
stop=0;
|
||||
}
|
||||
else if(input=='l' || input==KEY_RIGHT)
|
||||
jmove(0,+1);
|
||||
else if(input=='j' || input==KEY_LEFT )
|
||||
jmove(0,-1);
|
||||
else if(input=='k' || input==KEY_UP)
|
||||
rotate();
|
||||
else if(input=='p'){
|
||||
mvaddstr(LINES-2,COLS/2-15,"Paused - Press a key to continue ");
|
||||
refresh();
|
||||
nocbreak();
|
||||
cbreak();
|
||||
getch();
|
||||
halfdelay(DELAY);
|
||||
}
|
||||
else if(input=='q')
|
||||
goto Lose;
|
||||
else if(input==' ')
|
||||
while( (falls=fall()) )
|
||||
stop=0;
|
||||
else{
|
||||
falls=fall();
|
||||
stop=0;
|
||||
}
|
||||
draw();
|
||||
}
|
||||
combo=1;
|
||||
while(explode(combo)){ // explode, fall, explode, fall until nothing is left
|
||||
combo++;
|
||||
while(fall());
|
||||
draw();
|
||||
}
|
||||
}
|
||||
Lose:
|
||||
nocbreak();
|
||||
endwin();
|
||||
printf("%s _Jewels_ %s\n",jwstr,jwstr);
|
||||
printf("You have scored %ld points.\n",score);
|
||||
showscores(scorewrite(score));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
361
sources/mines.c
Normal file
361
sources/mines.c
Normal file
@ -0,0 +1,361 @@
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#define FLAG 9
|
||||
#define UNCLEAR 10
|
||||
/*
|
||||
|\/|
|
||||
| |INES
|
||||
|
||||
|
||||
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,flags;
|
||||
int untouched;
|
||||
int mscount;
|
||||
chtype colors[6]={0};
|
||||
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);
|
||||
}
|
||||
//display
|
||||
void draw(int sy,int sx,byte board[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(board[y][x]<0)
|
||||
prnt='.';
|
||||
else if(!board[y][x])
|
||||
prnt=' ';
|
||||
else if(board[y][x] < 8){
|
||||
attr |= colors[board[y][x]-1];
|
||||
prnt='0'+board[y][x];
|
||||
}
|
||||
else if(board[y][x]==9){
|
||||
attr |= colors[3];
|
||||
prnt='P';
|
||||
}
|
||||
else if(board[y][x]>9)
|
||||
prnt='?';
|
||||
|
||||
mvaddch(sy+1+y,sx+x*2+1,attr|prnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
//show the mines
|
||||
void drawmines(int sy,int sx,byte board[len][wid],bool mines[len][wid]){
|
||||
int y,x;
|
||||
for(y=0;y<len;y++){
|
||||
for(x=0;x<wid;x++){
|
||||
if(mines[y][x]){
|
||||
if(y==py&&x==px)
|
||||
mvaddch(sy+y+1,sx+x*2+1,'X');
|
||||
else if(board[y][x]==9)
|
||||
mvaddch(sy+y+1,sx+x*2+1,'%');
|
||||
else
|
||||
mvaddch(sy+y+1,sx+x*2+1,'*');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//place mines
|
||||
void mine(bool mines[len][wid]){
|
||||
int y=random()%len;
|
||||
int x=random()%wid;
|
||||
for(int n=0;n<mscount;n++){
|
||||
while(mines[y][x]){
|
||||
y=random()%len;
|
||||
x=random()%wid;
|
||||
}
|
||||
mines[y][x]=true;
|
||||
}
|
||||
}
|
||||
|
||||
bool click(byte board[len][wid],bool mines[len][wid],int ty,int tx){
|
||||
if(board[ty][tx]>=0 && board[ty][tx] <9)//it has been click()ed before
|
||||
return 0;
|
||||
else{//untouched
|
||||
if(board[ty][tx]==FLAG)
|
||||
flags--;
|
||||
board[ty][tx]=0;
|
||||
untouched--;
|
||||
|
||||
}
|
||||
int y,x;
|
||||
for(y=ty-1;y<ty+2;y++){
|
||||
if(y<0)
|
||||
y=0;
|
||||
if(y>=len)
|
||||
break;
|
||||
for (x=tx-1;x<tx+2;x++){
|
||||
if(x<0)
|
||||
x=0;
|
||||
if(x>=wid)
|
||||
break;
|
||||
|
||||
if(mines[y][x])
|
||||
board[ty][tx]++;
|
||||
}
|
||||
}
|
||||
|
||||
if(!board[ty][tx]){//there are no mines in the adjacent tiles
|
||||
for(y=ty-1;y<ty+2;y++){
|
||||
if(y<0)
|
||||
y=0;
|
||||
if(y>=len)
|
||||
break;
|
||||
for(x=tx-1;x<tx+2;x++){
|
||||
if(x<0)
|
||||
x=0;
|
||||
if(x>=wid)
|
||||
break;
|
||||
|
||||
click(board,mines,y,x);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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('\n');
|
||||
if(minput.bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED) )
|
||||
ungetch(' ');
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
mvprintw(1,0,"|\\/|");
|
||||
mvprintw(2,0,"| |INES");
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE CONTROLS ****");
|
||||
mvprintw(10,0,"YOU CAN ALSO USE THE MOUSE!");
|
||||
attroff(A_BOLD);
|
||||
mvprintw(4,0,"RETURN/ENTER : Examine for bombs");
|
||||
mvprintw(5,0,"SPACE : Flag/Unflag");
|
||||
mvprintw(6,0,"hjkl/ARROW KEYS : Move cursor");
|
||||
mvprintw(7,0,"q : Quit");
|
||||
mvprintw(8,0,"F1 & F2 : Help on controls & gameplay");
|
||||
mvprintw(9,0,"PgDn,PgUp,<,> : Scroll");
|
||||
mvprintw(12,0,"Press a key to continue");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
mvprintw(1,0,"|\\/|");
|
||||
mvprintw(2,0,"| |INES");
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE GAMEPLAY ****");
|
||||
attroff(A_BOLD);
|
||||
mvprintw(4,0,"Try to find the landmines in the field\n");
|
||||
printw("with logical reasoning: When you click\n");
|
||||
printw("on a tile ( a '.' here), numbers may show\n");
|
||||
printw("up that indicate the number of landmines\n");
|
||||
printw("in adjacent tiles; you should find and \n");
|
||||
printw("avoid the landmines based on them; and\n");
|
||||
printw("clicking on a landmine would make you\n");
|
||||
printw("lose the game.");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
int main(int argc, char** argv){
|
||||
signal(SIGINT,sigint_handler);
|
||||
if(argc>4 || (argc==2 && !strcmp("help",argv[1])) ){
|
||||
printf("Usage: %s [len wid [minescount]]\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 either too small or too big.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
len=wid=8;
|
||||
if(argc==4){
|
||||
if( !sscanf(argv[3],"%d",&mscount)){
|
||||
puts("Invalid input.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if( mscount<5 || mscount>= len*wid){
|
||||
puts("Too few/many mines.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
mscount = len*wid/6;
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
initscr();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
noecho();
|
||||
cbreak();
|
||||
keypad(stdscr,1);
|
||||
if(has_colors()){
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1,COLOR_BLUE,-1);
|
||||
init_pair(2,COLOR_GREEN,-1);
|
||||
init_pair(3,COLOR_YELLOW,-1);
|
||||
init_pair(4,COLOR_RED,-1);
|
||||
init_pair(5,COLOR_RED,COLOR_YELLOW);
|
||||
init_pair(6,COLOR_RED,COLOR_MAGENTA);
|
||||
for(byte b= 0;b<6;b++){
|
||||
colors[b]=COLOR_PAIR(b+1);
|
||||
}
|
||||
|
||||
}
|
||||
byte board[len][wid];
|
||||
bool mines[len][wid];
|
||||
char result[70];
|
||||
int input;
|
||||
int sy,sx;
|
||||
Start:
|
||||
sy=sx=0;
|
||||
py=px=0;
|
||||
untouched=len*wid;
|
||||
flags=0;
|
||||
curs_set(0);
|
||||
memset(board,-1,len*wid);
|
||||
memset(mines,false,len*wid);
|
||||
mine(mines);
|
||||
|
||||
while(1){
|
||||
erase();
|
||||
mvprintw(sy+1,sx+0,"|\\/| Flags:%d\n",flags);
|
||||
mvprintw(sy+2,sx+0,"| |INES Mines:%d\n",mscount);
|
||||
draw(sy+3,sx+0,board);
|
||||
refresh();
|
||||
if(untouched<=mscount){
|
||||
strcpy(result,"YAY!!");
|
||||
break;
|
||||
}
|
||||
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(input=='x' && getch()=='y' && getch()=='z' && getch()=='z' && getch()=='y' ){
|
||||
strcpy(result,"It is now pitch dark. If you proceed you will likely fall into a pit.");
|
||||
break;
|
||||
}
|
||||
if(input=='\n' && board[py][px] < 9){
|
||||
if(mines[py][px]){
|
||||
switch( random()%3){
|
||||
case 0:
|
||||
strcpy(result,"You lost The Game.");
|
||||
break;
|
||||
case 1:
|
||||
strcpy(result,"You exploded!");
|
||||
break;
|
||||
case 2:
|
||||
strcpy(result,"Bring your MRAP with you next time!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
click(board,mines,py,px);
|
||||
}
|
||||
if(input==' '){
|
||||
if(board[py][px] == -1){
|
||||
board[py][px]=FLAG;
|
||||
flags++;
|
||||
}
|
||||
else if(board[py][px] == FLAG){
|
||||
board[py][px]=UNCLEAR;
|
||||
flags--;
|
||||
}
|
||||
else if(board[py][px] == UNCLEAR)
|
||||
board[py][px]=-1;
|
||||
}
|
||||
}
|
||||
drawmines(sy+3,sx+0,board,mines);
|
||||
mvprintw(sy+len+5,sx+0,"%s Wanna play again?(y/n)",result);
|
||||
curs_set(1);
|
||||
input=getch();
|
||||
if(input != 'N' && input != 'n' && input != 'q')
|
||||
goto Start;
|
||||
endwin();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
534
sources/pipes.c
Normal file
534
sources/pipes.c
Normal file
@ -0,0 +1,534 @@
|
||||
#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 SPILL 13
|
||||
#define FLOWDELAY 5
|
||||
#define DELAY 3
|
||||
#define SAVE_TO_NUM 10
|
||||
#define SY 0
|
||||
#define SX 7
|
||||
/*
|
||||
_
|
||||
|_)
|
||||
| IPES
|
||||
|
||||
|
||||
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;
|
||||
typedef unsigned char bitbox;
|
||||
int len,wid,py,px,fy,fx;//p: pointer f: fluid
|
||||
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){
|
||||
mvprintw(0,0," _ ");
|
||||
mvprintw(1,0,"|_)");
|
||||
mvprintw(2,0,"| IPES");
|
||||
}
|
||||
|
||||
byte scorewrite(void){// only saves the top 10, returns the place in the chart
|
||||
bool deforno;
|
||||
if( !getenv("PP_SCORES") && (scorefile= fopen(PP_SCORES,"r")) ){
|
||||
deforno=1;
|
||||
}
|
||||
else{
|
||||
deforno=0;
|
||||
if( !(scorefile = fopen(getenv("PP_SCORES"),"r")) ){
|
||||
sprintf(error,"No accessible score files found. You can make an empty text file in %s/%s or set PP_SCORES to such a file to solve this.",getenv("HOME"),PP_SCORES);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char namebuff[SAVE_TO_NUM][60];
|
||||
long scorebuff[SAVE_TO_NUM];
|
||||
|
||||
memset(namebuff,0,SAVE_TO_NUM*60*sizeof(char) );
|
||||
memset(scorebuff,0,SAVE_TO_NUM*sizeof(long) );
|
||||
|
||||
long fuckingscore =0;
|
||||
char fuckingname[60]={0};
|
||||
byte location=0;
|
||||
|
||||
while( fscanf(scorefile,"%59s : %ld\n",fuckingname,&fuckingscore) == 2 && location<SAVE_TO_NUM ){
|
||||
strcpy(namebuff[location],fuckingname);
|
||||
scorebuff[location] = fuckingscore;
|
||||
location++;
|
||||
|
||||
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+. ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte itreached=location;
|
||||
byte ret = -1;
|
||||
bool wroteit=0;
|
||||
|
||||
for(location=0;location<=itreached && location<SAVE_TO_NUM-wroteit;location++){
|
||||
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;
|
||||
}
|
||||
|
||||
void showscores(byte playerrank){
|
||||
erase();
|
||||
logo();
|
||||
if(*error){
|
||||
mvaddstr(SY,SX,error);
|
||||
mvprintw(SY+1,SX,"However, your score is %ld.",score);
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
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){
|
||||
byte a = (len-9)/2;
|
||||
attron(A_BOLD);
|
||||
mvprintw(SY,SX, "**** ***");
|
||||
mvprintw(SY+len+1,SX,"***********************");
|
||||
attroff(A_BOLD);
|
||||
attron(green);
|
||||
mvprintw(SY,SX+4,"CONGRATULATIONS!");
|
||||
attroff(green);
|
||||
|
||||
mvprintw(SY+a+1,SX," _____ You bet the");
|
||||
mvprintw(SY+a+2,SX," .' | previous");
|
||||
mvprintw(SY+a+3,SX," .' | record");
|
||||
mvprintw(SY+a+4,SX," | .| | of");
|
||||
mvprintw(SY+a+5,SX," |.' | |%11ld",formerscore);
|
||||
mvprintw(SY+a+6,SX," | | held by");
|
||||
mvprintw(SY+a+7,SX," ___| |___%7s!",formername);
|
||||
mvprintw(SY+a+8,SX," | |");
|
||||
mvprintw(SY+a+9,SX," |____________|");
|
||||
mvprintw(len+2,0,"Game over! Press a key to proceed:");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
logo();
|
||||
}
|
||||
|
||||
}
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," HIGH");
|
||||
mvprintw(4,0,"SCORES");
|
||||
attroff(A_BOLD);
|
||||
//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){
|
||||
move(SY+1+rank,SX+1);
|
||||
attron(green);
|
||||
if(rank == playerrank)
|
||||
printw(">>>");
|
||||
printw("%d",rank+1);
|
||||
attroff(green);
|
||||
printw(") %s : %ld",pname,pscore);
|
||||
rank++;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
//move in direction
|
||||
void MID(bitbox direction){
|
||||
switch(direction){
|
||||
case UP:
|
||||
fy--;
|
||||
break;
|
||||
case DOWN:
|
||||
fy++;
|
||||
break;
|
||||
case LEFT:
|
||||
fx--;
|
||||
break;
|
||||
case RIGHT:
|
||||
fx++;
|
||||
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){
|
||||
for(int y=0;y<=len;y++){
|
||||
mvaddch(SY+y,SX,ACS_VLINE);
|
||||
mvaddch(SY+y,SX+wid+1,ACS_VLINE);
|
||||
}
|
||||
for(int x=0;x<=wid;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+1,ACS_URCORNER);
|
||||
mvaddch(SY+len+1,SX+wid+1,ACS_LRCORNER);
|
||||
}
|
||||
//this generates the pipes...
|
||||
bitbox pipegen(void){
|
||||
if(random()%17){//17 so all forms have the same chance
|
||||
byte a=random()%4;
|
||||
byte b;
|
||||
do{
|
||||
b=random()%4;
|
||||
}while(b==a);
|
||||
return (1 << a) | ( 1 << b);
|
||||
}
|
||||
else
|
||||
return CROSSOVER;//sum of all directions, the crossover
|
||||
|
||||
}
|
||||
//.. 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;
|
||||
for(y=0;y<len;y++){
|
||||
for(x=0;x<wid;x++){
|
||||
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){
|
||||
MEVENT minput;
|
||||
getmouse(&minput);
|
||||
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');
|
||||
}
|
||||
//peacefully close when ^C is pressed
|
||||
void sigint_handler(int x){
|
||||
endwin();
|
||||
puts("Quit.");
|
||||
exit(x);
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
logo();
|
||||
attron(A_BOLD);
|
||||
mvprintw(SY,SX+5,"-* *-");
|
||||
mvprintw(3,0," HELP");
|
||||
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);
|
||||
mvprintw(SY+1,SX,"RETURN/ENTER : Place/Replace a pipe");
|
||||
mvprintw(SY+2,SX,"hjkl/ARROW KEYS : Move cursor");
|
||||
mvprintw(SY+3,SX,"p : Pause");
|
||||
mvprintw(SY+4,SX,"q : Quit");
|
||||
mvprintw(SY+5,SX,"f : Toggle fast flow");
|
||||
mvprintw(SY+6,SX,"g : Go! (End the countdown.)");
|
||||
mvprintw(SY+6,SX,"F1 & F2 : Help on controls & gameplay");
|
||||
mvprintw(SY+9,SX,"Press a key to continue");
|
||||
refresh();
|
||||
while(getch()==ERR);
|
||||
erase();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
logo();
|
||||
attron(A_BOLD);
|
||||
mvprintw(SY,SX+5,"-* *-");
|
||||
mvprintw(3,0," HELP");
|
||||
mvprintw(4,0," PAGE");
|
||||
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();
|
||||
}
|
||||
int main(int argc, char** argv){
|
||||
signal(SIGINT,sigint_handler);
|
||||
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;
|
||||
}
|
||||
initscr();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
time_t tstart , now, lasttime, giventime=len*wid/4;
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
bitbox direction,board[len][wid];
|
||||
int input;
|
||||
byte foo;
|
||||
bool flow,fast;
|
||||
Start: //TODO in all the games, some things have to be moved behind the start . definize sy and sx
|
||||
flow=0;
|
||||
fast=0;
|
||||
score=0;
|
||||
memset(error,0,150);
|
||||
memset(board,0,len*wid);
|
||||
fy=1+(random()%(len-2) );
|
||||
fx=1+(random()%(wid-2) );
|
||||
board[fy][fx]= 1 << (random()%4);
|
||||
direction= board[fy][fx];
|
||||
board[fy][fx]|=FILLED;
|
||||
for(foo=0;foo<5;foo++)
|
||||
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);
|
||||
}
|
||||
for(foo=0;foo<5;foo++)
|
||||
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);
|
||||
score++;
|
||||
if(fast)
|
||||
score++;
|
||||
}
|
||||
else
|
||||
goto End;
|
||||
board[fy][fx]|=FILLED;
|
||||
}
|
||||
|
||||
input = getch();
|
||||
if( input == KEY_F(1) || input=='?' ){
|
||||
help();
|
||||
if(!flow)
|
||||
tstart += time(NULL)-now;
|
||||
}
|
||||
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 )
|
||||
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 == '\n' && !(board[py][px] & FILLED) ){
|
||||
if(board[py][px])
|
||||
score-=3;
|
||||
board[py][px]=tocome[0];
|
||||
for(foo=0;foo<4;foo++)
|
||||
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);
|
||||
mvprintw(3,0," OOPS!");
|
||||
attroff(A_BOLD|green);
|
||||
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;
|
||||
}
|
318
sources/rabbithole.c
Normal file
318
sources/rabbithole.c
Normal file
@ -0,0 +1,318 @@
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#define UP 1
|
||||
#define RIGHT 2
|
||||
#define DOWN 4
|
||||
#define LEFT 8
|
||||
#define VISITED 16
|
||||
#define CARROT 32
|
||||
/*
|
||||
_
|
||||
|_)
|
||||
| \ABBITHOLE
|
||||
|
||||
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;
|
||||
typedef unsigned char bitbox;
|
||||
int len,wid,py,px;
|
||||
|
||||
chtype colors[6]={0};
|
||||
|
||||
typedef struct point{
|
||||
int y;
|
||||
int x;
|
||||
} point;
|
||||
|
||||
point MID(int y,int x,bitbox direction){//move in direction
|
||||
point pt = {y,x};
|
||||
switch(direction){
|
||||
case UP:
|
||||
pt.y--;
|
||||
return pt;
|
||||
case DOWN:
|
||||
pt.y++;
|
||||
return pt;
|
||||
case LEFT:
|
||||
pt.x--;
|
||||
return pt;
|
||||
case RIGHT:
|
||||
pt.x++;
|
||||
return pt;
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
void rectangle(int sy,int sx){
|
||||
for(int y=0;y<=len*2;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*2,sx+x,ACS_HLINE);
|
||||
}
|
||||
mvaddch(sy,sx,ACS_ULCORNER);
|
||||
mvaddch(sy+len*2,sx,ACS_LLCORNER);
|
||||
mvaddch(sy,sx+wid*2,ACS_URCORNER);
|
||||
mvaddch(sy+len*2,sx+wid*2,ACS_LRCORNER);
|
||||
}
|
||||
|
||||
void draw(int sy,int sx,bitbox board[len][wid]){
|
||||
int y,x;
|
||||
bitbox d;
|
||||
chtype prnt;
|
||||
point pt;
|
||||
for(y=0;y<len;y++){
|
||||
for(x=0;x<wid;x++){
|
||||
prnt=0;
|
||||
if( board[y][x] & CARROT )
|
||||
prnt='%'|A_BOLD|colors[3];
|
||||
else if(y==py && x==px)
|
||||
prnt= 'r'|A_REVERSE;
|
||||
if( board[y][x] & VISITED ){
|
||||
if(y!=py || x!=px)
|
||||
prnt='.'|A_REVERSE;
|
||||
for(d=1;d<32;d=d << 1){
|
||||
if(board[y][x] & d){
|
||||
pt=MID(sy+1+y*2,sx+x*2+1,d);
|
||||
mvaddch(pt.y,pt.x,' '|A_REVERSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(prnt)
|
||||
mvaddch(sy+1+y*2,sx+x*2+1,prnt);
|
||||
}
|
||||
}
|
||||
rectangle(sy,sx);
|
||||
}
|
||||
void make_maze(bitbox board[len][wid],point f){
|
||||
byte ds_tried=0;
|
||||
byte dnumber=random()%4;
|
||||
bitbox direction= 1 << (dnumber);
|
||||
while( direction == board[f.y][f.x] )
|
||||
direction= 1 << (dnumber=random()%4);
|
||||
|
||||
point pt = MID(f.y,f.x,direction);
|
||||
while(ds_tried<4){
|
||||
if(pt.y<0 || pt.y==len || pt.x<0 || pt.x==wid || board[pt.y][pt.x])
|
||||
;
|
||||
else{ //if the tile exists and is empty
|
||||
board[f.y][f.x] |= direction;
|
||||
board[pt.y][pt.x]= 1 << ( (dnumber+2)%4 );//direction's reverse
|
||||
make_maze(board,pt);//recursion
|
||||
}
|
||||
direction= 1 << (dnumber= (dnumber+1)%4 );
|
||||
pt= MID(f.y,f.x,direction);
|
||||
ds_tried++;
|
||||
}
|
||||
}
|
||||
void carrotify(bitbox board[len][wid],int count){
|
||||
int y,x,c=count;
|
||||
while(c){
|
||||
y=random()%len;
|
||||
x=random()%wid;
|
||||
while( board[y][x] & CARROT ){
|
||||
y=random()%len;
|
||||
x=random()%wid;
|
||||
}
|
||||
board[y][x] |= CARROT;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
mvprintw(0,0," _ ");
|
||||
mvprintw(1,0,"|_)");
|
||||
mvprintw(2,0,"| \\ABBITHOLE");
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE CONTROLS ****");
|
||||
mvprintw(8,0,"YOU CAN ALSO USE THE MOUSE!");
|
||||
attroff(A_BOLD);
|
||||
mvprintw(4,0,"hjkl/ARROW KEYS : Move cursor");
|
||||
mvprintw(5,0,"q : Quit");
|
||||
mvprintw(6,0,"F1 & F2: Help on controls & gameplay (viewing these pages doesn't pause the timer!)");
|
||||
mvprintw(7,0,"PgDn,PgUp,<,> : Scroll");
|
||||
mvprintw(10,0,"Press a key to continue");
|
||||
|
||||
refresh();
|
||||
while ( getch()==ERR );
|
||||
erase();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
mvprintw(0,0," _ ");
|
||||
mvprintw(1,0,"|_)");
|
||||
mvprintw(2,0,"| \\ABBITHOLE");
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE GAMEPLAY ****");
|
||||
attroff(A_BOLD);
|
||||
move(4,0);
|
||||
printw("Try to gather all the carrots in the maze\n");
|
||||
printw("in the given time. The determining factors\n");
|
||||
printw("are your choice of paths and the speed of\n ");
|
||||
printw("your fingers.\n");
|
||||
refresh();
|
||||
while ( getch()==ERR );
|
||||
erase();
|
||||
}
|
||||
void sigint_handler(int x){
|
||||
endwin();
|
||||
puts("Quit.");
|
||||
exit(x);
|
||||
}
|
||||
int main(int argc, char** argv){
|
||||
bool autoset=0;
|
||||
signal(SIGINT,sigint_handler);
|
||||
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>800 || wid>800){
|
||||
puts("At least one of your given dimensions is either too small or too big.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
autoset=1;
|
||||
}
|
||||
initscr();
|
||||
if(autoset){
|
||||
if((LINES-7)/2 < 10)
|
||||
len=10;
|
||||
else
|
||||
len=(LINES-7)/2;
|
||||
|
||||
if((COLS-5)/2 < 20)
|
||||
wid=20;
|
||||
else
|
||||
wid=(COLS-5)/2;
|
||||
}
|
||||
int carrot_count= (len*wid)/50;
|
||||
int carrots_found;
|
||||
time_t tstart , now, giventime=len*wid/5;
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
point start={0,0};
|
||||
bitbox board[len][wid];
|
||||
int sy,sx;
|
||||
Start:
|
||||
tstart = time(NULL);
|
||||
carrots_found=0;
|
||||
initscr();
|
||||
curs_set(0);
|
||||
noecho();
|
||||
cbreak();
|
||||
halfdelay(3);
|
||||
keypad(stdscr,1);
|
||||
if(has_colors()){
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1,COLOR_BLUE,-1);
|
||||
init_pair(2,COLOR_GREEN,-1);
|
||||
init_pair(3,COLOR_YELLOW,-1);
|
||||
init_pair(4,COLOR_RED,-1);
|
||||
init_pair(5,COLOR_RED,COLOR_YELLOW);
|
||||
init_pair(6,COLOR_RED,COLOR_MAGENTA);
|
||||
for(byte b= 0;b<6;b++){
|
||||
colors[b]=COLOR_PAIR(b+1);
|
||||
}
|
||||
|
||||
}
|
||||
sy=sx=0;
|
||||
py=px=0;
|
||||
memset(board,0,len*wid);
|
||||
make_maze(board,start);
|
||||
carrotify(board,carrot_count);
|
||||
int input;
|
||||
while(1){
|
||||
board[py][px] |= VISITED;
|
||||
if( board[py][px] & CARROT ){
|
||||
carrots_found++;
|
||||
board[py][px] &= ~CARROT;
|
||||
}
|
||||
now=time(NULL);
|
||||
erase();
|
||||
mvprintw(sy+0,sx+0," _ ");
|
||||
mvprintw(sy+1,sx+0,"|_) Time left :%ld",giventime-(now-tstart));
|
||||
mvprintw(sy+2,sx+0,"| \\ABBITHOLE Carrots left :%d",carrot_count-carrots_found);
|
||||
draw(sy+3,sx+0,board);
|
||||
refresh();
|
||||
if(carrots_found==carrot_count || now-tstart == giventime){
|
||||
flushinp();
|
||||
break;
|
||||
}
|
||||
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=='k' || input==KEY_UP) && py>0 && (board[py][px]&UP) )
|
||||
py--;
|
||||
if( (input=='j' || input==KEY_DOWN) && py<len-1 && (board[py][px]&DOWN) )
|
||||
py++;
|
||||
if( (input=='h' || input==KEY_LEFT) && px>0 && (board[py][px]&LEFT) )
|
||||
px--;
|
||||
if( (input=='l' || input==KEY_RIGHT) && px<wid-1 && (board[py][px]&RIGHT) )
|
||||
px++;
|
||||
if( input=='q')
|
||||
sigint_handler(0);
|
||||
if( board[py][px] & CARROT ){
|
||||
carrots_found++;
|
||||
board[py][px] &= ~CARROT;
|
||||
}
|
||||
}
|
||||
End:
|
||||
nocbreak();
|
||||
cbreak();
|
||||
draw(3,0,board);
|
||||
refresh();
|
||||
if(carrots_found==carrot_count)
|
||||
mvprintw(len*2+4,0,"YAY!!");
|
||||
else
|
||||
mvprintw(len*2+4,0,"You gathered %2.1f%% of the carrots in %d seconds.",(float) carrots_found*100/carrot_count,giventime);
|
||||
|
||||
printw(" Wanna play again?(y/n)");
|
||||
curs_set(1);
|
||||
input=getch();
|
||||
if(input == 'Y' || input == 'y')
|
||||
goto Start;
|
||||
else if( input!= 'N' && input!= 'n' && input!='q')
|
||||
goto End;
|
||||
endwin();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
399
sources/reversi.c
Normal file
399
sources/reversi.c
Normal file
@ -0,0 +1,399 @@
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
/*
|
||||
_
|
||||
|_)
|
||||
| \EVERSI
|
||||
|
||||
|
||||
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;
|
||||
byte py,px;//cursor
|
||||
const char piece[2] = "OX";
|
||||
char game[8][8];//main board
|
||||
byte computer[2] = {0,0};
|
||||
byte score[2];//set by header()
|
||||
|
||||
void rectangle(byte sy,byte sx){
|
||||
for(byte y=0;y<=8+1;y++){
|
||||
mvaddch(sy+y,sx,ACS_VLINE);
|
||||
mvaddch(sy+y,sx+8*2,ACS_VLINE);
|
||||
}
|
||||
for(byte x=0;x<=8*2;x++){
|
||||
mvaddch(sy,sx+x,ACS_HLINE);
|
||||
mvaddch(sy+8+1,sx+x,ACS_HLINE);
|
||||
}
|
||||
mvaddch(sy,sx,ACS_ULCORNER);
|
||||
mvaddch(sy+8+1,sx,ACS_LLCORNER);
|
||||
mvaddch(sy,sx+8*2,ACS_URCORNER);
|
||||
mvaddch(sy+8+1,sx+8*2,ACS_LRCORNER);
|
||||
}
|
||||
|
||||
void header(void){//abuse, used to count the pieces on each side too
|
||||
score[0]=score[1]=0;
|
||||
for(byte y=0;y<8;y++){
|
||||
for(byte x=0;x<8;x++){
|
||||
if(game[y][x]){
|
||||
if(game[y][x]==piece[0])
|
||||
score[0]++;
|
||||
else
|
||||
score[1]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
mvaddch(0,1, '_');
|
||||
mvprintw(1,0,"|_) %2d:%2d",score[0],score[1]);
|
||||
mvprintw(2,0,"| \\EVERSI ");
|
||||
}
|
||||
|
||||
void draw(byte sy,byte sx){//the game's board
|
||||
rectangle(sy,sx);
|
||||
chtype attr ;
|
||||
for(byte y=0;y<8;y++){
|
||||
for(byte x=0;x<8;x++){
|
||||
attr=A_NORMAL;
|
||||
if(y==py && x==px)
|
||||
attr |= A_STANDOUT;
|
||||
if(game[y][x])
|
||||
mvaddch(sy+1+y,sx+x*2+1,attr|game[y][x]);
|
||||
else
|
||||
mvaddch(sy+1+y,sx+x*2+1,attr|'.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool can_reverse(byte ty , byte tx,char board[8][8],char piece){//can place a piece there?
|
||||
byte y,x,count;
|
||||
if(board[ty][tx])
|
||||
return false;
|
||||
for(byte dy=-1;dy<2;dy++){ //changes the direction
|
||||
for(byte dx=-1;dx<2;dx++){
|
||||
if(dx==0&&dy==0)//it would be itself
|
||||
dx=1;
|
||||
count=0;
|
||||
y=ty+dy;
|
||||
x=tx+dx;
|
||||
while(1){
|
||||
if(y<0 || y>=8 ||x<0 || x>=8){//reaches edges of the board
|
||||
count=0;
|
||||
break;
|
||||
}
|
||||
if(!board[y][x]){//gap
|
||||
count=0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(board[y][x]!=piece){
|
||||
count++;
|
||||
y+=dy;
|
||||
x+=dx;
|
||||
}
|
||||
else
|
||||
break;//same color
|
||||
}
|
||||
if(count)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reverse(byte ty,byte tx,char board[8][8],char piece){//place a piece there
|
||||
board[ty][tx]=piece;
|
||||
byte y,x;
|
||||
for(byte dy=-1;dy<2;dy++){//changes the direction
|
||||
for(byte dx=-1;dx<2;dx++){
|
||||
if(dy==0 && dx==0)
|
||||
dx=1;
|
||||
y=ty+dy;
|
||||
x=tx+dx;
|
||||
while(1){
|
||||
if(y<0 || y>=8 || x<0 || x>=8)
|
||||
break;
|
||||
if(!board[y][x])
|
||||
break;
|
||||
if(board[y][x]!=piece){
|
||||
y+=dy;
|
||||
x+=dx;
|
||||
}
|
||||
else{ //of same kind
|
||||
while(y!=ty || x!=tx){ //reverse the disks
|
||||
board[y][x]=piece;
|
||||
y-=dy;
|
||||
x-=dx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool can_move(char board[8][8],char piece){//can move at all?
|
||||
for(byte y=0;y<8;y++)
|
||||
for(byte x=0;x<8;x++)
|
||||
if(can_reverse(y,x,board,piece))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
double advantage(char board[8][8],char piece){
|
||||
double own=0;
|
||||
double opp=0;
|
||||
for(byte y=0;y<8;y++){
|
||||
for(byte x=0;x<8;x++){
|
||||
if(board[y][x]){
|
||||
if(board[y][x]==piece){
|
||||
own++;
|
||||
if( ((y==7 || y==0)&&(x!=7 && x!=0)) || ((x==7 || x==0)&&(y!=7 && y!=0)) )//edges
|
||||
own+=100;
|
||||
if( (y==7 || y==0)&&(x==7 || x==0) )//corners
|
||||
own+=10000;
|
||||
}
|
||||
else{
|
||||
opp++;
|
||||
if( ((y==7 || y==0)&&(x!=7 && x!=0)) || ((x==7 || x==0)&&(y!=7 && y!=0)) )
|
||||
opp+=100;
|
||||
if( (y==7 || y==0)&&(x==7 || x==0) )
|
||||
opp+=10000;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return own/opp;
|
||||
}
|
||||
|
||||
void cp(char A[8][8],char B[8][8]){//copy the board A to B
|
||||
for(byte y=0;y<8;y++)
|
||||
for(byte x=0;x<8;x++)
|
||||
B[y][x]=A[y][x];
|
||||
}
|
||||
|
||||
double decide(char board[8][8],char piece,char opponet,byte depth){//AI algorithm
|
||||
if(!can_move(board,piece))
|
||||
return 0;
|
||||
char plan[8][8];
|
||||
double adv,bestadv;
|
||||
adv=bestadv=0;
|
||||
byte besty,bestx;
|
||||
for(byte y=0;y<8;y++){
|
||||
for(byte x=0;x<8;x++){
|
||||
if(can_reverse(y,x,board,piece) ){
|
||||
cp(board,plan);//backtrack
|
||||
reverse(y,x,plan,piece);
|
||||
if(depth){
|
||||
adv= decide(plan,opponet,piece,depth-1);//least benefit for the opponet
|
||||
if(adv) //the opponet can make a move
|
||||
adv = 1/adv;
|
||||
else
|
||||
adv=advantage(plan,piece);
|
||||
}
|
||||
else
|
||||
adv=advantage(plan,piece);
|
||||
if(adv>bestadv){
|
||||
bestadv=adv;
|
||||
besty=y;
|
||||
bestx=x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reverse(besty,bestx,board,piece);//do the move
|
||||
return bestadv;
|
||||
}
|
||||
//peacefully close when ^C is pressed
|
||||
void sigint_handler(int x){
|
||||
endwin();
|
||||
puts("Quit.");
|
||||
exit(x);
|
||||
}
|
||||
void mouseinput(void){
|
||||
MEVENT minput;
|
||||
getmouse(&minput);
|
||||
if( minput.y-4 <8 && minput.x-1<16){
|
||||
py=minput.y-4;
|
||||
px=(minput.x-1)/2;
|
||||
}
|
||||
else
|
||||
return;
|
||||
if(minput.bstate & BUTTON1_CLICKED)
|
||||
ungetch('\n');
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
header();
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE CONTROLS ****");
|
||||
mvprintw(8,0,"YOU CAN ALSO USE THE MOUSE!");
|
||||
attroff(A_BOLD);
|
||||
mvprintw(4,0,"RETURN/ENTER : Put the piece");
|
||||
mvprintw(5,0,"hjkl/ARROW KEYS : Move cursor");
|
||||
mvprintw(6,0,"q : Quit");
|
||||
mvprintw(7,0,"F1 & F2 : Help on controls & gameplay");
|
||||
mvprintw(10,0,"Press a key to continue");
|
||||
curs_set(1);
|
||||
getch();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
header();
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE GAMEPLAY ****");
|
||||
attroff(A_BOLD);
|
||||
move(4,0);
|
||||
printw("Players take turns placing disks on the board:\n\n");
|
||||
printw("1) Any pieces of the opponet's color that is bounded\n");
|
||||
printw(" in a straight line between the piece just placed and\n");
|
||||
printw(" another piece of the current player's color would turn\n");
|
||||
printw(" to the current player's color.\n\n");
|
||||
printw("2) You can only put pieces if at least one of your \n");
|
||||
printw(" opponet's pieces turns into your color.\n\n");
|
||||
printw("3) The game ends when neither side can do a move and\n");
|
||||
printw(" the player with more pieces wins.\n");
|
||||
getch();
|
||||
}
|
||||
int main(int argc , char** argv){
|
||||
int depth=2;
|
||||
if(argc>2){
|
||||
printf("Usage:%s [AIpower]",argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(argc==2){
|
||||
if(sscanf(argv[1],"%d",&depth) && depth<128 && 0<depth)
|
||||
;//already done in sscanf
|
||||
else{
|
||||
printf("That should be a number from 1 to 127.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
signal(SIGINT,sigint_handler);
|
||||
initscr();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
noecho();
|
||||
cbreak();
|
||||
keypad(stdscr,1);
|
||||
int input;
|
||||
printw("Black plays first:\n");
|
||||
printw("Choose type of the white player (H/c)\n");
|
||||
input=getch();
|
||||
if(input == 'c'){
|
||||
computer[0]=depth;
|
||||
printw("Computer.\n");
|
||||
}
|
||||
else{
|
||||
computer[1]=0;
|
||||
printw("Human.\n");
|
||||
}
|
||||
printw("Choose type of the black player(h/C)\n");
|
||||
input=getch();
|
||||
if(input == 'h'){
|
||||
computer[1]=0;
|
||||
printw("Human.\n");
|
||||
}
|
||||
else{
|
||||
computer[1]=depth;
|
||||
printw("Computer.\n");
|
||||
}
|
||||
Start:
|
||||
curs_set(0);
|
||||
py=px=0;
|
||||
memset(game,0,64);
|
||||
bool turn=0;
|
||||
bool resign=0;
|
||||
byte cantmove=0;
|
||||
game[3][3]=piece[0];
|
||||
game[4][4]=piece[0];
|
||||
game[3][4]=piece[1];
|
||||
game[4][3]=piece[1];
|
||||
|
||||
Turn:
|
||||
erase();
|
||||
flushinp();
|
||||
header();
|
||||
draw(3,0);
|
||||
refresh();
|
||||
if(cantmove >=2)//both sides cant move, the game ends
|
||||
goto End;
|
||||
|
||||
turn = !turn;
|
||||
if(computer[turn]){
|
||||
if(can_move(game,piece[turn])){
|
||||
mvprintw(13,0,"Thinking...");
|
||||
refresh();
|
||||
decide(game,piece[turn],piece[!turn],computer[turn]);
|
||||
cantmove=0;
|
||||
}
|
||||
else
|
||||
cantmove++;
|
||||
goto Turn;
|
||||
|
||||
}
|
||||
|
||||
if(!can_move(game,piece[turn])){
|
||||
cantmove++;
|
||||
goto Turn;
|
||||
}
|
||||
else{
|
||||
cantmove=0;
|
||||
while(1){ //human control
|
||||
erase();
|
||||
header();
|
||||
draw(3,0);
|
||||
input=getch();
|
||||
if( input==KEY_F(1) || input=='?' )
|
||||
help();
|
||||
if( input==KEY_F(2) )
|
||||
gameplay();
|
||||
if( input==KEY_MOUSE )
|
||||
mouseinput();
|
||||
if( (input=='k' || input==KEY_UP) && py>0)
|
||||
py--;
|
||||
if( (input=='j' || input==KEY_DOWN) && py<7)
|
||||
py++;
|
||||
if( (input=='h' || input==KEY_LEFT) && px>0)
|
||||
px--;
|
||||
if( (input=='l' || input==KEY_RIGHT) && px<7)
|
||||
px++;
|
||||
if( input=='q'){
|
||||
resign=1;
|
||||
goto End;
|
||||
}
|
||||
if(input=='\n'){
|
||||
if(can_reverse(py,px,game,piece[turn])){
|
||||
reverse(py,px,game,piece[turn]);
|
||||
goto Turn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
End:
|
||||
if(resign)
|
||||
mvprintw(13,0,"You resigned.");
|
||||
else if(score[0]==score[1])
|
||||
mvprintw(13,0,"Draw!!");
|
||||
else if(score[0] > score[1])
|
||||
mvprintw(13,0,"'%c' won.",piece[0]);
|
||||
else
|
||||
mvprintw(13,0,"'%c' won.",piece[1]);
|
||||
|
||||
printw(" Wanna play again?(y/n)");
|
||||
curs_set(1);
|
||||
input=getch();
|
||||
if( resign){
|
||||
if (input=='Y' || input=='y')
|
||||
goto Start;
|
||||
}
|
||||
else if(input != 'N' && input != 'n' && input != 'q')
|
||||
goto Start;
|
||||
endwin();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
403
sources/sos.c
Normal file
403
sources/sos.c
Normal file
@ -0,0 +1,403 @@
|
||||
#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;
|
||||
}
|
441
sources/sudoku.c
Normal file
441
sources/sudoku.c
Normal file
@ -0,0 +1,441 @@
|
||||
#include <curses.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h> //to seed random
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <math.h>
|
||||
/*
|
||||
_
|
||||
(_
|
||||
_)UDOKU
|
||||
|
||||
|
||||
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
|
||||
|
||||
NOTE: This program is only made for entertainment porpuses. The puzzles are generated by randomly clearing tiles on the table and are guaranteed to have a solution , but are not guaranteed to have only one unique solution.
|
||||
*/
|
||||
typedef signed char byte;
|
||||
byte wait=0, waitcycles=0;
|
||||
byte size,s;//s=size*size
|
||||
byte py,px;
|
||||
byte diff;
|
||||
unsigned int filled;
|
||||
chtype colors[6]={0};
|
||||
void cross(byte sy,byte sx,chtype start,chtype middle,chtype end){ //to simplify drawing tables. doesn't draw a cross (why did I choose that name?)
|
||||
mvaddch(sy,sx,start);
|
||||
byte f = 2*size;
|
||||
for(char n=1;n<size;n++){
|
||||
mvaddch(sy,sx+f,middle);
|
||||
f+=2*size;
|
||||
}
|
||||
mvaddch(sy,sx+f,end);
|
||||
}
|
||||
|
||||
void table(byte sy,byte sx){ //empty table
|
||||
byte l;//line
|
||||
for(l=0;l<=size;l++){
|
||||
for(byte y=0;y<=s+size;y++)
|
||||
mvaddch(sy+y,sx+l*size*2,ACS_VLINE);
|
||||
for(byte x=0;x<=s*2;x++)
|
||||
mvaddch(sy+(size+1)*l,sx+x,ACS_HLINE);
|
||||
}
|
||||
cross(sy,sx,ACS_ULCORNER,ACS_TTEE,ACS_URCORNER);
|
||||
for(l=1;l<size;l++)
|
||||
cross(sy+l*size+l,sx,ACS_LTEE,ACS_PLUS,ACS_RTEE);
|
||||
cross(sy+l*size+l,sx,ACS_LLCORNER,ACS_BTEE,ACS_LRCORNER);
|
||||
}
|
||||
|
||||
byte sgn2int(char sgn){
|
||||
if('0'<sgn && sgn <='9')
|
||||
return sgn-'0';
|
||||
if('a'<=sgn && sgn <='z')
|
||||
return sgn-'a'+10;
|
||||
if('A'<=sgn && sgn <= 'Z')
|
||||
return sgn-'A'+36;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char int2sgn(byte num){// convert integer to representing sign
|
||||
if(0< num && num <= 9)
|
||||
return num+'0';
|
||||
else if(10<=num && num <=35)
|
||||
return num-10+'a';
|
||||
else if(36<=num && num <=51)
|
||||
return num-36+'A';
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isvalid(byte ty,byte tx,char board[s][s]){ //is it allowed to place that char there?
|
||||
char t= board[ty][tx];
|
||||
if(!t)
|
||||
return 0;
|
||||
byte y,x;
|
||||
for(y=0;y<s;y++){
|
||||
if(board[y][tx] == t && y!=ty)
|
||||
return 0;
|
||||
}
|
||||
for(x=0;x<s;x++){
|
||||
if(board[ty][x] == t && x!= tx)
|
||||
return 0;
|
||||
}
|
||||
byte sy=size*(ty/size);//square
|
||||
byte sx=size*(tx/size);
|
||||
for(y=0;y<size;y++){
|
||||
for(x=0;x<size;x++){
|
||||
if(board[sy+y][sx+x]==t && sy+y != ty && sx+x != tx)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void genocide(char board[s][s],char victim){
|
||||
for(byte y=0;y<s;y++)
|
||||
for(byte x=0;x<s;x++)
|
||||
if(board[y][x]==victim)
|
||||
board[y][x]=0;
|
||||
}
|
||||
bool fill_with(char board[s][s],char fillwith){//returns 1 on failure
|
||||
byte firstx,x,tries=0;
|
||||
Again:
|
||||
tries++;
|
||||
if (tries>s)
|
||||
return 1;
|
||||
for(byte y=0;y<s;y++){//there should be only one occurence of a number in a row, and this function makes use of this fact to improve generation speed
|
||||
firstx=x=random()%s;
|
||||
while(1){
|
||||
if(!board[y][x]){
|
||||
board[y][x]=fillwith;
|
||||
if(isvalid(y,x,board)){
|
||||
break;
|
||||
}
|
||||
else{
|
||||
board[y][x]=0;
|
||||
goto Next;
|
||||
}
|
||||
}
|
||||
else{
|
||||
Next:
|
||||
x++;
|
||||
if(x==s)
|
||||
x=0;
|
||||
if(x==firstx){
|
||||
genocide(board,fillwith);
|
||||
goto Again;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
return 0;
|
||||
}
|
||||
void fill(char board[s][s]){
|
||||
for(byte num=1;num<=s;num++){//it fills random places in the board with 1s, then random places in the remaining space with 2s and so on...
|
||||
if(num==4){//something that randomly happens
|
||||
wait=(wait+1)%60;
|
||||
if(!wait && waitcycles<3)
|
||||
waitcycles++;
|
||||
}
|
||||
if ( fill_with(board,int2sgn(num) ) ){
|
||||
memset(board,0,s*s);
|
||||
num=0;
|
||||
mvaddstr(0,0,"My algorithm sucks, so you need to wait a bit ");//with animated dots to entertain the waiter
|
||||
if(waitcycles==3)
|
||||
mvaddstr(2,0,"(You can set SUDOKU_FASTGEN if you just want to see if it works)");
|
||||
move(0,45);
|
||||
for(byte b=0;b<wait;b+=10)
|
||||
addch('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
void swap(char board[s][s],char A,char B){
|
||||
byte y,x;
|
||||
for(y=0;y<s;y++){
|
||||
for(x=0;x<s;x++){
|
||||
if(board[y][x]==A)
|
||||
board[y][x]=B;
|
||||
else if(board[y][x]==B)
|
||||
board[y][x]=A;
|
||||
}
|
||||
}
|
||||
}
|
||||
void justfill(char board[s][s]){//sometimes fill() gets too much , and you just want a 49x49 sudoku puzzle of any quality
|
||||
byte y,x,k;//k is here to minimize calls to isvalid()
|
||||
for(y=0;y<s;y++){//fill with 1,2,3,4...
|
||||
k=1;
|
||||
for(x=0;x<s;x++){
|
||||
board[y][x]=int2sgn(k);
|
||||
while(!isvalid(y,x,board)){
|
||||
board[y][x]=int2sgn(k=k+1);
|
||||
if(k>s)
|
||||
board[y][x]=int2sgn(k=1);
|
||||
}
|
||||
k++;
|
||||
if(k>s)
|
||||
k=1;
|
||||
}
|
||||
}
|
||||
for(byte n=0;n<s*2;n++)//randomize
|
||||
swap(board,int2sgn(1+(random()%s)),int2sgn(1+(random()%s)) );
|
||||
}
|
||||
void mkpuzzle(char board[s][s],char empty[s][s],char game[s][s]){//makes a puzzle to solve
|
||||
byte y,x;
|
||||
for(y=0;y<s;y++){
|
||||
for(x=0;x<s;x++){
|
||||
if( !(random()%diff) ){
|
||||
empty[y][x]=board[y][x];
|
||||
game[y][x]=board[y][x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void header(byte sy,byte sx){
|
||||
mvaddch(sy, sx+1, '_');
|
||||
mvprintw(sy+1,sx,"(_ Solved:%d/%d",filled,s*s);
|
||||
mvprintw(sy+2,sx," _)UDOKU Left :%d/%d",s*s-filled,s*s);
|
||||
}
|
||||
|
||||
void draw(byte sy,byte sx,char empty[s][s],char game[s][s]){
|
||||
chtype attr;
|
||||
table(sy,sx);
|
||||
filled=0;
|
||||
for(byte y=0;y<s;y++){
|
||||
for(byte x=0;x<s;x++){
|
||||
attr=A_NORMAL;
|
||||
if(x==px && y==py)
|
||||
attr |= A_STANDOUT;
|
||||
if(empty[y][x])
|
||||
attr |= A_BOLD;
|
||||
if(game[y][x]){
|
||||
if(!isvalid(y,x,game))
|
||||
attr |= colors[5];
|
||||
else{
|
||||
attr |= colors[game[y][x]%5];
|
||||
filled++;
|
||||
}
|
||||
mvaddch(sy+y+y/size+1,sx+x*2+1,attr|game[y][x]);
|
||||
}
|
||||
else
|
||||
mvaddch(sy+y+y/size+1,sx+x*2+1,attr|' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sigint_handler(int x){
|
||||
endwin();
|
||||
puts("Quit.");
|
||||
exit(x);
|
||||
}
|
||||
void mouseinput(int sy, int sx){
|
||||
MEVENT m;
|
||||
getmouse(&m);
|
||||
if( m.y < (3+1+size+s)-sy && m.x<(2*s+1)-sx ){//it's a shame to include math.h only for round() but it was the only moral way to make gcc shut up
|
||||
py= round( (float)(size*(m.y-4-sy))/(size+1) );//these are derived from the formulas in draw() by simple algebra
|
||||
px=(m.x-1-sx)/2;
|
||||
}
|
||||
else
|
||||
return;
|
||||
if(m.bstate & BUTTON1_CLICKED)
|
||||
ungetch('\n');
|
||||
if(m.bstate & (BUTTON2_CLICKED|BUTTON3_CLICKED) )
|
||||
ungetch(' ');
|
||||
}
|
||||
void help(void){
|
||||
erase();
|
||||
header(0,0);
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE CONTROLS ****");
|
||||
mvprintw(13,0,"YOU CAN ALSO USE THE MOUSE!");
|
||||
attroff(A_BOLD);
|
||||
mvprintw(4,0,"1 - %c : Enter number/character",int2sgn(s));
|
||||
mvprintw(5,0,"SPACE : Clear tile");
|
||||
mvprintw(6,0,"ARROW KEYS : Move cursor");
|
||||
mvprintw(7,0,"q : Quit");
|
||||
mvprintw(8,0,"n : New board");
|
||||
mvprintw(9,0,"r : Restart");
|
||||
if(size>4)
|
||||
printw(" (some of these alphabet controls maybe overrided in certain sizes)");
|
||||
mvprintw(10,0,"? : Hint (not like in other games)");
|
||||
mvprintw(11,0,"F1 & F2: Help on controls & gameplay");
|
||||
mvprintw(12,0,"PgDn,PgUp,<,> : Scroll");
|
||||
mvprintw(15,0,"Press a key to continue");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
void gameplay(void){
|
||||
erase();
|
||||
header(0,0);
|
||||
attron(A_BOLD);
|
||||
mvprintw(3,0," **** THE GAMEPLAY ****");
|
||||
attroff(A_BOLD);
|
||||
move(4,0);
|
||||
printw("Fill the table with digits");
|
||||
if(size>3)
|
||||
printw(" (and characters) \n");
|
||||
else
|
||||
addch('\n');
|
||||
|
||||
printw("so that all the rows, columns and smaller subregions \n");
|
||||
printw("contain all of the digits from 1 to ");
|
||||
if(size<=3){
|
||||
addch(int2sgn(s));
|
||||
addch('.');
|
||||
}
|
||||
if(size>3){
|
||||
addch('9');
|
||||
printw(" and all\nthe alphabet letters from 'a' to '%c'.",int2sgn(s));
|
||||
}
|
||||
printw("\n\nPress a key to continue.");
|
||||
refresh();
|
||||
getch();
|
||||
erase();
|
||||
}
|
||||
int main(int argc,char** argv){
|
||||
signal(SIGINT,sigint_handler);
|
||||
if(argc>4 || (argc==2 && !strcmp("help",argv[1])) ){
|
||||
printf("Usage: %s [size [ difficulty]] \n",argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(argc>1 ){
|
||||
if(strlen(argv[1])>1 || argv[1][0]-'0'>7 || argv[1][0]-'0'< 2){
|
||||
printf("2 <= size <= 7\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
size = *argv[1]-'0';
|
||||
}
|
||||
else
|
||||
size=3;
|
||||
if(argc>2){
|
||||
if (strlen(argv[2])>1 || argv[2][0]-'0'>4 || argv[2][0]-'0'<= 0 ){
|
||||
printf("1 <= diff <=4\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
diff = *argv[2]-'0'+1;
|
||||
}
|
||||
else
|
||||
diff=2;
|
||||
bool fastgen= !(!getenv("SUDOKU_FASTGEN"));
|
||||
initscr();
|
||||
mousemask(ALL_MOUSE_EVENTS,NULL);
|
||||
noecho();
|
||||
cbreak();
|
||||
keypad(stdscr,1);
|
||||
srandom(time(NULL)%UINT_MAX);
|
||||
if( has_colors() ){
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1,COLOR_YELLOW,-1);
|
||||
init_pair(2,COLOR_GREEN,-1);
|
||||
init_pair(3,COLOR_BLUE,-1);
|
||||
init_pair(4,COLOR_CYAN,-1);
|
||||
init_pair(5,COLOR_MAGENTA,-1);
|
||||
init_pair(6,COLOR_RED,-1);
|
||||
for(byte b=0;b<6;b++){
|
||||
colors[b]=COLOR_PAIR(b+1);
|
||||
}
|
||||
}
|
||||
s= size*size;
|
||||
char board[s][s];
|
||||
char empty[s][s];
|
||||
char game[s][s];
|
||||
int input=0;
|
||||
int sy,sx;
|
||||
Start:
|
||||
sy=sx=0;
|
||||
erase();
|
||||
curs_set(0);
|
||||
filled =0;
|
||||
memset(board,0,s*s);
|
||||
memset(empty,0,s*s);
|
||||
memset(game,0,s*s);
|
||||
if(fastgen)
|
||||
justfill(board);
|
||||
else
|
||||
fill(board);
|
||||
mkpuzzle(board,empty,game);
|
||||
py=px=0;
|
||||
|
||||
while(1){
|
||||
erase();
|
||||
draw(sy+3,sx+0,empty,game);
|
||||
header(sy+0,sx+0);
|
||||
refresh();
|
||||
if(filled == s*s)
|
||||
break;
|
||||
input = getch();
|
||||
if( input==KEY_PPAGE && LINES< s+size+3){//the board starts in 3
|
||||
sy+=10;
|
||||
if(sy>0)
|
||||
sy=0;
|
||||
}
|
||||
if( input==KEY_NPAGE && LINES< s+size+3){
|
||||
sy-=10;
|
||||
if(sy< -(s+size+3) )
|
||||
sy=-(s+size+3);
|
||||
}
|
||||
if( input=='<' && COLS< s*2){
|
||||
sx+=10;
|
||||
if(sx>0)
|
||||
sx=0;
|
||||
}
|
||||
if( input=='>' && COLS< s*2){
|
||||
sx-=10;
|
||||
if(sx< -(s*2))
|
||||
sx=-(s*2);
|
||||
}
|
||||
if(input == KEY_F(1))
|
||||
help();
|
||||
if(input == KEY_F(2))
|
||||
gameplay();
|
||||
if(input == KEY_MOUSE)
|
||||
mouseinput(sy,sx);
|
||||
if(input == KEY_UP && py)
|
||||
py--;
|
||||
if(input == KEY_DOWN && py<s-1)
|
||||
py++;
|
||||
if(input == KEY_LEFT && px)
|
||||
px--;
|
||||
if(input == KEY_RIGHT && px<s-1)
|
||||
px++;
|
||||
if(!empty[py][px]){
|
||||
if(input == ' ' )
|
||||
game[py][px]=0;
|
||||
else if(input<=CHAR_MAX && sgn2int(input) && sgn2int(input)<=s )
|
||||
game[py][px]=input;
|
||||
}
|
||||
if( (input=='q' && size<= 5) || input=='Q')
|
||||
sigint_handler(EXIT_SUCCESS);
|
||||
if(input=='n'&& size <= 4)
|
||||
goto Start;
|
||||
if(input=='r'&& size <= 5){
|
||||
byte y,x;
|
||||
for(y=0;y<s;y++)
|
||||
for(x=0;x<s;x++)
|
||||
game[y][x]=empty[y][x];
|
||||
}
|
||||
|
||||
if(input=='?')
|
||||
game[py][px]=board[py][px];
|
||||
if(input == 'X' && getch()=='Y' && getch()=='Z' && getch()=='Z' && getch()=='Y'){
|
||||
byte y,x;
|
||||
for(y=0;y<s;y++)
|
||||
for(x=0;x<s;x++)
|
||||
game[y][x]=board[y][x];
|
||||
}
|
||||
}
|
||||
mvprintw(sy+s+size+4,sx+0,"Yay!! Wanna play again?(y/n)");
|
||||
curs_set(1);
|
||||
input=getch();
|
||||
if(input != 'N' && input != 'n' && input != 'q')
|
||||
goto Start;
|
||||
endwin();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user