Last active
August 20, 2016 03:54
-
-
Save ddrpa/943fc3f0642a201fc1f303f3c8f5e8b7 to your computer and use it in GitHub Desktop.
greed single player and multiplayer version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* # README | |
* for more information, visit(CN) | |
* http://ddrpa.github.io/2016/greed-for-win32-and-nix.html | |
* This is a This is a curses-based clone of the DOS free-ware game Greed | |
* Inspired by Eric S. Raymond's Implement on Ubuntu | |
* But I add element from TRON under multiplayer mode | |
* The goal of this game is to try to eat as much as possible of the board \ | |
* before munching yourself into a corner. | |
* ## HOWTO COMPILE | |
* Please compile this code with C99-support on, \ | |
* works on Windows(cl/icc) and Linux(gcc/clang) | |
* ## HOWTO PLAY | |
* Enter "binary-name" to play solo as player 1; | |
* Enter "binary-name" -m or "binary-name" m to play with another one; | |
* Player 1 use WASD to move, while player 2 use IJKL(not VI-Like); | |
* In solo mode, you keep playing untill no way to go; | |
* In multiplay mode, after both players died, one with higher score is winner. | |
* AMeT 2016-8-10 | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <time.h> | |
#ifdef WIN32 | |
#include <conio.h> | |
#include <Windows.h> | |
#else | |
#include <unistd.h> | |
#include <termios.h> | |
#endif | |
// FieldHeight + 5 < Terminal's line limit on one screen on Linux, | |
// or the position of curses will be unpredictable | |
#define FieldHeight 34 | |
#define FieldWidth 80 | |
typedef struct { | |
short int NodeValue; | |
bool NodeVisable; | |
} Node; | |
typedef struct { | |
short int PosX; | |
short int PosY; | |
char KeyGroup[8]; | |
int Scores; | |
bool Susurvival; | |
bool Up; | |
bool Down; | |
bool Left; | |
bool Right; | |
} Player; | |
typedef struct { | |
short int XDirMove; | |
short int YDirMove; | |
} XYMovement; | |
enum Direction { | |
Invalid = 0, Up = 1, Down, Left, Right | |
} MoveDirection; | |
// The Implement of gotoxy() for Windows Uses a Windows Console Function instead of ncurses library | |
// Thanks to Jerry Coffin | |
// http://stackoverflow.com/questions/2732292/setting-the-cursor-position-in-a-win32-console-application | |
// The version for UNIX-Like System Using ANSI/VT100 Terminal Control Escape Sequences | |
// Improved on Robin Thomas's version | |
// https://www.quora.com/What-is-gotoxy-function-used-in-c | |
void gotoxy(int x, int y){ | |
#ifdef WIN32 | |
COORD pos = { x, y }; | |
HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE); | |
SetConsoleCursorPosition(output, pos); | |
#else | |
printf("\x1b[%d;%df", y +1, x + 1); | |
#endif | |
} | |
#ifndef WIN32 | |
// Some Implements of getch() for UNIX-Like System, On Windows Just Use _getch() in conio.h | |
// Or you can try ncurses library, which has a getch() implements( also have gotoxy etc.,) | |
// but remember to init something firist( I haven't used it yet) | |
// This One Thanks to Falcon Momot & anon | |
// http://stackoverflow.com/questions/421860/capture-characters-from-standard-input-without-waiting-for-enter-to-be-pressed | |
// May cause Printf() blocked until next getch() calling, unknown reason | |
/*char _getch(){ | |
char buf = 0; | |
struct termios old = {0}; | |
if (tcgetattr(0, &old) < 0) | |
perror("tcsetattr()"); | |
old.c_lflag &= ~ICANON; | |
old.c_lflag &= ~ECHO; | |
old.c_cc[VMIN] = 1; | |
old.c_cc[VTIME] = 0; | |
if (tcsetattr(0, TCSANOW, &old) < 0) | |
perror("tcsetattr ICANON"); | |
if (read(0, &buf, 1) < 0) | |
perror ("read()"); | |
old.c_lflag |= ICANON; | |
old.c_lflag |= ECHO; | |
if (tcsetattr(0, TCSADRAIN, &old) < 0) | |
perror ("tcsetattr ~ICANON"); | |
return (buf); | |
}*/ | |
// This One use same idea but work | |
// Thanks to 追_梦_者 | |
// http://blog.csdn.net/it_dream_er/article/details/45896679 | |
char _getch(void){ | |
char buf; | |
system("stty -echo"); | |
system("stty -icanon"); | |
buf = getchar(); | |
system("stty icanon"); | |
system("stty echo"); | |
return buf; | |
} | |
#endif | |
void ClearScreen(void){ | |
#ifdef WIN32 | |
system("cls"); | |
#else | |
printf("\x1b[2J"); | |
gotoxy(0, 0); | |
#endif | |
} | |
int RefreshScreen(Node** NodeMap, Player* p1, Player* p2){ | |
ClearScreen(); | |
for (int HCounter = 0; HCounter < FieldHeight; HCounter++){ | |
for (int WCounter = 0; WCounter < FieldWidth; WCounter++){ | |
if (NodeMap[HCounter][WCounter].NodeVisable){ | |
switch (NodeMap[HCounter][WCounter].NodeValue){ | |
#ifdef WIN32 | |
case 1: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); | |
putchar('1'); break; | |
case 2: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); | |
putchar('2'); break; | |
case 3: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE); | |
putchar('3'); break; | |
case 4: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN); | |
putchar('4'); break; | |
case 5: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE); | |
putchar('5'); break; | |
case 6: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE | FOREGROUND_RED); | |
putchar('6'); break; | |
case 7: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); | |
putchar('7'); break; | |
case 8: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY); | |
putchar('8'); break; | |
case 9: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); | |
putchar('9'); break; | |
#else | |
case 1:printf("\x1b[31m1\x1b[0m"); break; | |
case 2:printf("\x1b[32m2\x1b[0m"); break; | |
case 3:printf("\x1b[33m3\x1b[0m"); break; | |
case 4:printf("\x1b[34m4\x1b[0m"); break; | |
case 5:printf("\x1b[35m5\x1b[0m"); break; | |
case 6:printf("\x1b[36m6\x1b[0m"); break; | |
case 7:printf("\x1b[37m7\x1b[0m"); break; | |
case 8:printf("\x1b[31m8\x1b[0m"); break; | |
case 9:printf("\x1b[31m9\x1b[0m"); break; | |
#endif | |
default:break; | |
} | |
} | |
else { | |
if (p1->PosY == HCounter && p1->PosX == WCounter){ | |
#ifdef WIN32 | |
putchar('@'); | |
#else | |
printf("\x1b[44m@\x1b[0m"); | |
#endif | |
} | |
else if (p2 != NULL&&p2->PosY == HCounter && p2->PosX == WCounter){ | |
#ifdef WIN32 | |
putchar('%'); | |
#else | |
printf("\x1b[41m@\x1b[0m"); | |
#endif | |
} | |
else { | |
putchar(' '); | |
} | |
} | |
} | |
printf("\r\n"); | |
} | |
#ifdef WIN32 | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); | |
#endif | |
return 0; | |
} | |
char PlayerKeyPress(Player* player){ | |
char KeyPress = _getch(); | |
if (player->PosY > 0 && (KeyPress == player->KeyGroup[0] || KeyPress == player->KeyGroup[1])){ | |
if (!player->Up) | |
return (char)Invalid; | |
else | |
return (char)Up; | |
} | |
else if (player->PosY < (FieldHeight - 1) && (KeyPress == player->KeyGroup[2] || KeyPress == player->KeyGroup[3])){ | |
if (!player->Down) | |
return (char)Invalid; | |
else | |
return (char)Down; | |
} | |
else if (player->PosX > 0 && (KeyPress == player->KeyGroup[4] || KeyPress == player->KeyGroup[5])){ | |
if (!player->Left) | |
return (char)Invalid; | |
else | |
return (char)Left; | |
} | |
else if (player->PosX < (FieldWidth - 1) && (KeyPress == player->KeyGroup[6] || KeyPress == player->KeyGroup[7])){ | |
if (!player->Right) | |
return (char)Invalid; | |
else | |
return (char)Right; | |
} | |
else{ | |
return (char)Invalid; | |
} | |
} | |
int PlayerMakeMove(char* PlayerMovDir, Node** NodeMap, Player* player){ | |
short int movments = 0, steps = 0; | |
XYMovement XYDir = { 0, 0 }; | |
switch (*PlayerMovDir){ | |
case Up: XYDir.YDirMove = -1; break; | |
case Down: XYDir.YDirMove = 1; break; | |
case Left: XYDir.XDirMove = -1; break; | |
case Right:XYDir.XDirMove = 1; break; | |
default:break; | |
} | |
movments = NodeMap[player->PosY + XYDir.YDirMove][player->PosX + XYDir.XDirMove].NodeValue; | |
steps = movments; | |
while (steps){ | |
player->PosY += XYDir.YDirMove; | |
player->PosX += XYDir.XDirMove; | |
NodeMap[player->PosY][player->PosX].NodeVisable = false; | |
steps--; | |
} | |
return (int)movments; | |
} | |
bool IsAnyWayOut(Node** NodeMap, Player* player){ | |
short int Steps; | |
if (player->PosY > 0){ | |
Steps = NodeMap[player->PosY - 1][player->PosX].NodeValue; | |
if (player->PosY >= Steps){ | |
while (Steps){ | |
player->Up = true; | |
if (!NodeMap[player->PosY - Steps][player->PosX].NodeVisable){ | |
player->Up = false; | |
break; | |
} | |
Steps--; | |
} | |
} | |
else{ | |
player->Up = false; | |
} | |
} | |
else{ | |
player->Up = false; | |
} | |
if (player->PosY < (FieldHeight - 1)){ | |
Steps = NodeMap[player->PosY + 1][player->PosX].NodeValue; | |
if (player->PosY + Steps <= (FieldHeight - 1)){ | |
while (Steps){ | |
player->Down = true; | |
if (!NodeMap[player->PosY + Steps][player->PosX].NodeVisable){ | |
player->Down = false; | |
break; | |
} | |
Steps--; | |
} | |
} | |
else{ | |
player->Down = false; | |
} | |
} | |
else{ | |
player->Down = false; | |
} | |
if (player->PosX > 0){ | |
Steps = NodeMap[player->PosY][player->PosX - 1].NodeValue; | |
if (player->PosX >= Steps){ | |
while (Steps){ | |
player->Left = true; | |
if (!NodeMap[player->PosY][player->PosX - Steps].NodeVisable){ | |
player->Left = false; | |
break; | |
} | |
Steps--; | |
} | |
} | |
else{ | |
player->Left = false; | |
} | |
} | |
else{ | |
player->Left = false; | |
} | |
if (player->PosX < (FieldWidth - 1)){ | |
Steps = NodeMap[player->PosY][player->PosX + 1].NodeValue; | |
if (player->PosX + Steps <= (FieldWidth - 1)){ | |
while (Steps){ | |
player->Right = true; | |
if (!NodeMap[player->PosY][player->PosX + Steps].NodeVisable){ | |
player->Right = false; | |
break; | |
} | |
Steps--; | |
} | |
} | |
else{ | |
player->Right = false; | |
} | |
} | |
else{ | |
player->Right = false; | |
} | |
if (player->Up || player->Down || player->Left || player->Right){ | |
player->Susurvival = true; | |
} | |
else{ | |
player->Susurvival = false; | |
} | |
return 0; | |
} | |
int RefreshBanner(Player* p1,Player* p2){ | |
gotoxy(0, FieldHeight + 2); | |
for (int i = 0; i < FieldWidth; i++){ | |
putchar('='); | |
} | |
printf("\r\n"); | |
#ifdef WIN32 | |
printf("PLAYER 1 SCORES: %4d", p1->Scores); | |
#else | |
printf("\x1b[44mPLAYER 1 SCORES: %4d\x1b[0m", p1->Scores); | |
#endif | |
if (p2 != NULL){ | |
gotoxy(FieldWidth-21, FieldHeight + 3); | |
#ifdef WIN32 | |
printf("PLAYER 2 SCORES: %4d", p2->Scores); | |
#else | |
printf("\x1b[44mPLAYER 2 SCORES: %4d\x1b[0m", p2->Scores); | |
#endif | |
} | |
return 0; | |
} | |
int RefreshScreenPartly(Node** NodeMap, Player* p1, Player* p2){ | |
for (int HCounter = 0; HCounter < FieldHeight; HCounter++){ | |
for (int WCounter = 0; WCounter < FieldWidth; WCounter++){ | |
if (!NodeMap[HCounter][WCounter].NodeVisable){ | |
gotoxy(WCounter, HCounter); | |
if (p1->PosY == HCounter && p1->PosX == WCounter){ | |
#ifdef WIN32 | |
putchar('@'); | |
#else | |
printf("\x1b[44m@\x1b[0m"); | |
#endif | |
} | |
else if (p2!=NULL && p2->PosY == HCounter && p2->PosX == WCounter){ | |
#ifdef WIN32 | |
putchar('%'); | |
#else | |
printf("\x1b[41m@\x1b[0m"); | |
#endif | |
} | |
else{ | |
putchar(' '); | |
} | |
} | |
} | |
} | |
RefreshBanner(p1,p2); | |
return 0; | |
} | |
int PrintHelp(void){ | |
ClearScreen(); | |
printf("You Got Those Help Info by:\r\n"); | |
printf("* Enter \'greed -h\' or \'greed h\' to get help\r\n"); | |
printf("* Enter Wrong arguments\r\n"); | |
printf("Just enter \'greed\' to play solo as player 1;\r\n"); | |
printf("Enter \'greed -m\' or \'greed m\' to play with another one;\r\n"); | |
printf("Player 1 use WASD to move, while player 2 use IJKL(not VI-Like);\r\n"); | |
printf("In solo mode, you keep playing untill no way to go;\r\n"); | |
printf("In multiplay mode, after both players died, \r\n one with higher score is winner.\r\n\r\n"); | |
printf("This is a curses-based clone of the DOS free-ware game Greed \r\n inspired by Eric S. Raymond's Implement on Ubuntu. The goal of this \r\n game is to try to eat as much as possible of the board before \r\n munching yourself into a corner.\r\n"); | |
return 0; | |
} | |
int InitPlayer2(Player* p2, int p2PosX){ | |
p2->PosX = p2PosX; | |
p2->PosY = FieldHeight / 2; | |
p2->KeyGroup[0] = 'I'; | |
p2->KeyGroup[1] = 'i'; | |
p2->KeyGroup[2] = 'K'; | |
p2->KeyGroup[3] = 'k'; | |
p2->KeyGroup[4] = 'J'; | |
p2->KeyGroup[5] = 'j'; | |
p2->KeyGroup[6] = 'L'; | |
p2->KeyGroup[7] = 'l'; | |
p2->Scores = 0; | |
p2->Susurvival = true; | |
p2->Up = true; | |
p2->Down = true; | |
p2->Left = true; | |
p2->Right = true; | |
return 0; | |
} | |
//int BlinkPlayer(Player* p1, Player* p2){ | |
// if (p1 != NULL){ | |
// gotoxy(p1->PosX, p1->PosY); | |
//#ifdef WIN32 | |
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_GREEN); | |
// putchar('@'); | |
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_BLUE); | |
//#else | |
// printf("\x1b[44m\x1b[5m@\x1b[0m"); | |
//#endif | |
// } | |
// if (p2 != NULL){ | |
// gotoxy(p2->PosX, p2->PosY); | |
//#ifdef WIN32 | |
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_GREEN); | |
// putchar('%'); | |
// SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_BLUE); | |
//#else | |
// printf("\x1b[41m\x1b[5m@\x1b[0m"); | |
//#endif | |
// } | |
// return 0; | |
//} | |
// Blink effect is not ideal | |
int BlinkPlayer(Player* p1, Player* p2){ | |
/*#ifndef WIN32 | |
if (p1 != NULL){ | |
gotoxy(p1->PosX, p1->PosY); | |
printf("\x1b[44m\x1b[5m@\x1b[0m"); | |
} | |
if (p2 != NULL){ | |
gotoxy(p2->PosX, p2->PosY); | |
printf("\x1b[41m\x1b[5m@\x1b[0m"); | |
} | |
#endif*/ | |
return 0; | |
} | |
int main(int argc, char* argv[]){ | |
Player player1 = { FieldWidth / 2, FieldHeight / 2, { 'W', 'w', 'S', 's', 'A', 'a', 'D', 'd' }, 0, true, true, true, true, true }; | |
Player* pplayer2 = NULL; | |
Node** Field = (Node**)malloc(FieldHeight*sizeof(Node*)); | |
for (int i = 0; i < FieldHeight; i++){ | |
Field[i] = (Node*)malloc(FieldWidth*sizeof(Node)); | |
} | |
if (argc != 2 && argc != 1){ | |
PrintHelp(); | |
return 0; | |
} | |
if (argc==2){ | |
char arg = 0; | |
if (argv[1][1] != '\0') | |
arg = argv[1][1]; | |
else | |
arg=argv[1][0]; | |
switch (arg){ | |
case 'm': | |
player1.PosX = FieldWidth / 3; | |
pplayer2 = (Player*)malloc(sizeof(Player)); | |
InitPlayer2(pplayer2, FieldWidth * 2 / 3); | |
break; | |
default:PrintHelp(); return 0; | |
} | |
} | |
srand(time(NULL)); | |
int RandVal = 8; | |
for (int HCounter = 0; HCounter < FieldHeight; HCounter++){ | |
for (int WCounter = 0; WCounter < FieldWidth; WCounter++){ | |
Field[HCounter][WCounter].NodeValue = (rand() % RandVal) + 1; | |
Field[HCounter][WCounter].NodeVisable = true; | |
} | |
} | |
Field[player1.PosY][player1.PosX].NodeVisable = false; | |
if (pplayer2 != NULL){ | |
Field[pplayer2->PosY][pplayer2->PosX].NodeVisable = false; | |
} | |
RefreshScreen(Field, &player1,pplayer2); | |
do{ | |
if(player1.Susurvival){ | |
char P1MovDir = 0; | |
IsAnyWayOut(Field, &player1); | |
do{ | |
BlinkPlayer(&player1, NULL); | |
P1MovDir = PlayerKeyPress(&player1); | |
} while (!P1MovDir); | |
player1.Scores += PlayerMakeMove(&P1MovDir, Field, &player1); | |
IsAnyWayOut(Field, &player1); | |
RefreshScreenPartly(Field, &player1, pplayer2); | |
if(pplayer2){ | |
IsAnyWayOut(Field, pplayer2); | |
} | |
} | |
if (pplayer2 != NULL&&pplayer2->Susurvival){ | |
char P2MovDir = 0; | |
IsAnyWayOut(Field, pplayer2); | |
if(!pplayer2->Susurvival)break; | |
do{ | |
BlinkPlayer(NULL,pplayer2); | |
P2MovDir = PlayerKeyPress(pplayer2); | |
} while (!P2MovDir); | |
pplayer2->Scores += PlayerMakeMove(&P2MovDir, Field, pplayer2); | |
IsAnyWayOut(Field, pplayer2); | |
IsAnyWayOut(Field, &player1); | |
RefreshScreenPartly(Field, &player1, pplayer2); | |
} | |
} while (player1.Susurvival || (pplayer2 && pplayer2->Susurvival)); | |
RefreshScreen(Field, &player1,pplayer2); | |
RefreshBanner(&player1, pplayer2); | |
if (!player1.Susurvival && !pplayer2){ | |
printf("\r\nPLAYER 1 LOSE"); | |
}else if(player1.Scores > pplayer2->Scores){ | |
printf("\r\nPLAYER 1 WIN"); | |
} | |
else if (player1.Scores < pplayer2->Scores){ | |
printf("\r\nPLAYER 2 WIN"); | |
} | |
else{ | |
printf("\r\nTIE"); | |
} | |
printf("\r\nPress [Q/q] to quit\r\n"); | |
free(pplayer2); | |
for (int i = 0; i < FieldHeight; i++){ | |
free(Field[i]); | |
} | |
free(Field); | |
char ch; | |
do{ | |
ch = _getch(); | |
} while (ch != 'q'&&ch != 'Q'); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment