Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ddrpa/943fc3f0642a201fc1f303f3c8f5e8b7 to your computer and use it in GitHub Desktop.
Save ddrpa/943fc3f0642a201fc1f303f3c8f5e8b7 to your computer and use it in GitHub Desktop.
greed single player and multiplayer version
/* # 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