Files
hangman/hangman.c
2017-05-03 19:38:10 +02:00

323 lines
8.4 KiB
C

#include <ncurses.h>
#include "hangman.h"
#include <stdlib.h> //atexit
#include <string.h>
#include <ctype.h> //tolower
#include <getopt.h>
#include <time.h> //time(null)
#include "prng.h"
#include <unistd.h> //usleep
int main(int argc, char **argv) {
const char *short_options = "w:hf:ct";
struct option long_options[] = {
{"word", required_argument, NULL, 'w'},
{"help", no_argument, NULL, 'h'},
{"file", required_argument, NULL, 'f'},
{"credits", no_argument, NULL, 'c'},
{"troll", no_argument, NULL, 't'}
};
int c, startscr = 1; /* Show startscreen by default */
char filename[255];
filename[0] = '\0';
//while ( (c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1 )
/* Initialization */
initscr();
atexit(quitProgram);
game_state *gs;
gs = malloc(sizeof(game_state));
initCoordinates(gs);
gs->allowedMoves = DEFAULTTRIES;
gs->moves = 0;
gs->guesses = 0;
gs->trollEnabled = 0;
curs_set(0);
while ( (c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1 ) {
switch (c) {
case 'w':
sprintf(gs->guessWord, "%s", optarg);
startscr = 0;
break;
case 'h':
showHelp(gs);
exit(0);
break;
case 'f':
/* Set filename */
sprintf(filename, "%s", optarg);
startscr = 0;
break;
case 'c':
break;
case 't':
/* troll option */
gs->trollEnabled = 1;
break;
default:
break;
}
}
InitializePRNG(time(NULL)); /* Initialize random number generator */
if (startscr) {
showStartScreen(gs);
}
/* start game */
initGuessWord(gs, filename);
startGame(gs);
getch();
free(gs);
return 0;
}
void startGame(game_state *gs) {
noecho(); /* Don't show the player input */
while (checkWin(gs)) {
updateScreen(gs);
playerInput(gs);
}
}
void quitProgram(void) {
endwin();
}
void showStartScreen(game_state *gs) {
mvprintw(gs->centery - 1, gs->centerx - 15, "Welcome to hangman in ncurses.");
mvprintw(gs->centery, gs->centerx - 5, "Have fun!");
mvprintw(gs->centery + 1, gs->centerx - 17, "https://gitlab.com/STRUCTiX/hangman");
refresh();
getch();
clear();
}
void updateScreen(game_state *gs) {
mvprintw(1, 1, "Remaining wrong guesses: %i", (gs->allowedMoves - gs->moves));
mvprintw(gs->maxy - 1, 1, "Press ctrl + c to exit.");
drawGuessWord(gs);
mvprintw(2, 1, "Wrong characters: %s", gs->wrongCharacters);
refresh();
}
void initCoordinates(game_state *gs) {
getmaxyx(stdscr, gs->maxy, gs->maxx);
gs->centery = gs->maxy / 2;
gs->centerx = gs->maxx / 2;
}
void initGuessWord(game_state *gs, char *filename) {
int i;
mvprintw(1, 1, "Please enter your word: ");
if (strlen(gs->guessWord) == 0) { /* Word can be set by arguments */
if (strlen(filename) == 0) {
/* Manual input */
curs_set(1);
getstr(gs->guessWord);
curs_set(0);
} else {
/* Random line from file */
readRandomLine(filename, gs->guessWord);
}
}
/* Make String all lowercase */
toLowerCase(gs->guessWord);
gs->wordLength = strlen(gs->guessWord);
for (i = 0; i < gs->wordLength; i++) {
if (gs->guessWord[i] == ' ') { //Check for spaces if it's a sentence
gs->currentWord[i] = ' ';
} else {
gs->currentWord[i] = '_';
}
}
clear(); //clear the screen
}
void toLowerCase(char *str) {
int length = strlen(str);
int i;
for (i = 0; i < length; i++) {
str[i] = tolower(str[i]);
}
}
void drawGuessWord(game_state *gs) {
int startpos = gs->centerx - gs->wordLength;
int i, wordpos = 0;
int switchspace = 0;
for (i = startpos; i < startpos + (gs->wordLength * 2); i++) {
if (switchspace) {
/* this will place a space */
mvprintw(gs->centery, i, " ");
switchspace = 0;
} else {
mvprintw(gs->centery, i, "%c", gs->currentWord[wordpos++]);
switchspace = 1;
}
}
refresh();
}
int playerInput(game_state *gs) {
char inp;
int i, found = 0;
inp = getch();
for (i = 0; i < gs->wordLength; i++) {
if (inp == gs->guessWord[i]) {
found++;
//break;
}
}
if (found) {
/* found a valid character */
if (fillCurrentWord(gs, inp)) {
//gs->moves++;
gs->guesses++;
trollHitScreen(gs, found);
}
} else {
/* no valid character found */
if (stackWrongCharacter(gs, inp)) {
gs->moves++;
gs->guesses++;
}
}
return 0;
}
int fillCurrentWord(game_state *gs, char validchar) {
int i, alreadyUsed = 0;
for (i = 0; i < gs->wordLength; i++) {
if (gs->currentWord[i] == validchar) {
alreadyUsed = 1;
break;
}
}
if (!alreadyUsed) {
for (i = 0; i < gs->wordLength; i++) {
if (gs->guessWord[i] == validchar) {
gs->currentWord[i] = validchar;
}
}
return 1;
} else {
return 0;
}
}
int stackWrongCharacter(game_state *gs, char wrongchar) {
int i, alreadyUsed = 0;
for (i = 0; i < gs->wordLength; i++) {
if (gs->wrongCharacters[i] == wrongchar) {
alreadyUsed = 1;
}
}
if (!alreadyUsed) {
gs->wrongCharacters[strlen(gs->wrongCharacters)] = wrongchar;
return 1;
} else {
return 0;
}
}
int checkWin(game_state *gs) {
if (strcmp(gs->guessWord, gs->currentWord) != 0 && gs->moves < gs->allowedMoves) {
/* next move */
return 1;
} else {
/* game end: decide if game is won or lost */
printGameStats(gs);
return 0;
}
}
void printGameStats(game_state *gs) {
clear();
if (gs->moves >= gs->allowedMoves) {
mvprintw(gs->centery, gs->centerx - 10, "Game lost. Solution:");
mvprintw(gs->centery + 1, gs->centerx - (gs->wordLength / 2), gs->guessWord);
} else {
char message[100];
sprintf(message, "Game won! Total guesses: %i", gs->guesses);
mvprintw(gs->centery, gs->centerx - (strlen(message) / 2), message);
sprintf(message, "Wrong guesses: %i, right/wrong ratio: %.2f", gs->moves, (float)(gs->guesses - gs->moves) / gs->moves);
mvprintw(gs->centery, gs->centerx - (strlen(message) / 2), message);
}
refresh();
}
void showHelp(game_state *gs) {
char *wordstring = "-w or --word: Enter the word or sentence as an argument";
char *helpstring = "-h or --help: Show this page";
mvprintw(gs->centery, centerDiff(gs->centerx, wordstring), wordstring);
mvprintw(gs->centery + 1, centerDiff(gs->centerx, helpstring), helpstring);
getch();
}
int centerDiff(int coordinate, char *str) {
int len = strlen(str);
return coordinate - (len / 2); /* Integer division */
}
void readRandomLine(char *file, char *result) {
long totalLength = 0, rand = 0;
FILE *fp = fopen(file, "r");
char c;
while ((c = fgetc(fp)) != EOF) {
if (c == '\n') ++totalLength;
}
rand = getrandom(0, totalLength);
fseek(fp, rand, SEEK_SET);
fgets(result, MAXWORDLENGTH, fp);
fclose(fp);
}
void trollHitScreen(game_state *gs, int hits) {
if (gs->trollEnabled) {
char *strings[] = {"Double Hit", "Triple Hit", "Multi Hit", \
"Ultra Hit", "Monster Hit", "Rampage", \
"Unstoppable", "Wicked sick", "Godlike"};
int selection[] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
int i, len, z, found = 0;
for (i = 0; i < 9; i++) {
if (selection[i] == hits) {
found = 1;
break;
}
}
if (found) {
flash(); /* flash the screen */
mvprintw(5, centerDiff(gs->centerx, strings[i]), strings[i]);
refresh();
/* sleep and vanish */
usleep(500000);
len = strlen(strings[i]);
for (z = centerDiff(gs->centerx, strings[i]); z < gs->centerx + len; z++) {
mvprintw(5, z, " ");
}
}
}
}