diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | COPYING | 2 | ||||
| -rw-r--r-- | GAMESPEC | 45 | ||||
| -rw-r--r-- | INPUT | 9 | ||||
| -rw-r--r-- | MAP | 22 | ||||
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | TRANSCRIPT | 57 | ||||
| -rw-r--r-- | chatbot.c | 128 | ||||
| -rw-r--r-- | chatbot.h | 21 | ||||
| -rw-r--r-- | gtnw.c | 426 | ||||
| -rw-r--r-- | gtnw.h | 21 | ||||
| -rw-r--r-- | joshua.c | 106 | ||||
| -rw-r--r-- | joshua.h | 27 | ||||
| -rw-r--r-- | location.h | 51 | ||||
| -rw-r--r-- | map.h | 40 | ||||
| -rw-r--r-- | server.c | 159 | ||||
| -rw-r--r-- | strings.c | 97 | ||||
| -rw-r--r-- | strings.h | 33 | ||||
| -rw-r--r-- | util.c | 81 | ||||
| -rw-r--r-- | util.h | 30 |
21 files changed, 1372 insertions, 3 deletions
@@ -4,6 +4,11 @@ *.obj *.elf +# Junk files +a.out +wargames +*~ + # Libraries *.lib *.a @@ -21,3 +26,4 @@ *.i*86 *.x86_64 *.hex +>>>>>>> 242a6e2d8edd375da6b02d7e2ece1b41d0f8de54 @@ -0,0 +1,2 @@ +Most of the files are licensed under the GNU GPL version 3 or (at your option), any later version: <http://www.gnu.org/licenses/gpl.html> +MAP and map.h are licensed under the Creative Commons Attribution-Sharealike 4.0 International License: <http://creativecommons.org/licenses/by-sa/4.0/>
\ No newline at end of file diff --git a/GAMESPEC b/GAMESPEC new file mode 100644 index 0000000..638a131 --- /dev/null +++ b/GAMESPEC @@ -0,0 +1,45 @@ +This file contains a formal definition of Global Thermonuclear War. +=======GAMEPLAY======== +The player with population still remaining after the other has been wiped out is the winner. +If both players are wiped out, it is a tie. +Each turn, the player can either: + - Launch ICBMs + - Negociate peace terms + - Surrender + +The behavior of each of these options is described below. + +ICBM launch: + - Both sides get 6 ICBMs per turn. + - The USSR gets 7 if it goes first. + - When an ICBM is launched, there are 5 possible results: + - Miss: 0 casualties + - Marginal: 20%+1000 casualties + - Minor: 40%+2500 casualties + - Major: 60%+5000 casualties + - Critical: 100% casualties + - The probabilities of each of these options is outlined below: + - Miss - 10% + - Marginal - 20% + - Minor - 30% + - Major - 30% + - Critical - 10% + - After the turn, the cities targeted will have one of these symbols in it: + - Miss - O + - Marginal - x + - Minor - * + - Major - X + - Critical - ! + +Negociation: + - There are three possible outcomes: + - Peace/Progress - 20% + - Surprise attack - 20% + - No progress - 60% + - Progress must be made at least 5 times if peace is to be achieved + +Surrender: + - The other player wins + +=======GRAPHICS======= +Each turn, a map is displayed with the populations of the player's cities in a table below. @@ -0,0 +1,9 @@ +Joshua +Hello. +I'm fine. How are you? +People sometimes make mistakes. +Love to. How about Global Thermonuclear War? +Later. Let's play Global Thermonuclear War. +2 +Las Vegas +Seattle @@ -0,0 +1,22 @@ +/* + * This file is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/. + * + * Attribution should be given to Franklin Wei. + */ + + . + __________--^-^-\. ____ __----/^\. +|\/. \__. ___/ || ___/ _/._-_ . +| \. /. /. __ __/ /__/ \/^^\___-__. +| L-^-/. /. \.\_-- \. +| / _/ _/\/. +|. | _/. __ __/ + \. /. / ___/.// + \__ / | / \/. + \________ __ _____.\. \_. ____--_ /\_ \ + \__. / V. \ \ \__ _/. \_/ // + \ /. \/. \. _/. // + \_/. \_/ + + UNITED STATES SOVIET UNION diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c24d8a6 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +SOURCES=joshua.o server.o util.o chatbot.o gtnw.o strings.o +HEADERS=chatbot.h gtnw.h joshua.h location.h strings.h map.h util.h +CFLAGS=-std=gnu99 -g -O3 +wargames: $(SOURCES) $(HEADERS) + g++ $(SOURCES) $(CXXFLAGS) -o wargames +all: wargames Makefile +clean: + rm -f $(SOURCES) a.out wargames *~ @@ -1,4 +1,4 @@ -wargames-server -=============== +WarGames +======== -Want to run the WOPR over telnet? This server does just that! +Ever wanted your very own WOPR? This is what you're looking for! diff --git a/TRANSCRIPT b/TRANSCRIPT new file mode 100644 index 0000000..9a6d843 --- /dev/null +++ b/TRANSCRIPT @@ -0,0 +1,57 @@ +<<<<< This is my transcript of the scene in WarGames (1983) where David has dialed into the WOPR and started playing Global Thermonuclear War. >>>>> +<<<<< All spaces, newlines, punctuation, etc. and made to be as accurate as possible >>>>> +LOGON: Joshua +<Random stuff> +<CLEAR> +GREETINGS PROFESSOR FALKEN. + +Hello. + + +HOW ARE YOU FEELING TODAY? + +I'm fine. How are you? + + +EXCELLENT. IT'S BEEN A LONG TIME. CAN YOU EXPLAIN +THE REMOVAL OF YOUR USER ACCOUNT ON 6/23/73? + +People sometimes make mistakes. (NOTE: in one scene, it appears that he typed "mistak" instead of "mistakes.") + + +YES THEY DO. SHALL WE PLAY A GAME? + +Love to. How about Global Thermonuclear War? + + +WOULDN'T YOU PREFER A GOOD GAME OF CHESS? + +Later. Let's play Global Thermonuclear War. + +FINE. +<CLEAR> +<US + USSR ASCII ART> + +UNITED STATES SOVIET UNION + +WHICH SIDE DO YOU WANT? + + 1. UNITED STATES + 2. SOVIET UNION + +PLEASE CHOOSE ONE: 2 +<CLEAR> +AWAITING FIRST STRIKE COMMAND (UNDERLINED) + + +PLEASE LIST PRIMARY TARGETS BY +CITY AND/OR COUNTY NAME: + +Las Vegas +Seattle + +<CLEAR> + +<ASCII ART OF US + USSR> +<US HAS TRAJECTORY HEADINGS OF ICBMS BOUND FOR LAS VEGAS AND SEATTLE> +<SUBS REPRESENTED BY BLINKING DOTS ARE OFF US SHORELINES>
\ No newline at end of file diff --git a/chatbot.c b/chatbot.c new file mode 100644 index 0000000..27bbdca --- /dev/null +++ b/chatbot.c @@ -0,0 +1,128 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include "gtnw.h" +#include "strings.h" +#include "util.h" +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +void do_chatbot(void) +{ + int stage=0; /* stage 0: i'm fine how are you... -> + stage 1: people sometimes make mistakes -> + stage 2: love to. how about global thermonuclear war? -> + stage 3: no lets play global thermonuclear war -> + stage 4: GLOBAL THERMONUCLEAR WAR!!! */ + while(1) + { + char buf[513]; + int ret=getnstr(buf, 512); + usleep(SLEEP_TIME*100); + if(ret==ERR) + { + print_string("\n\n"); + print_string("SORRY?"); + print_string("\n\n"); + } + else + { + allLower(buf); + remove_punct(buf); + bool valid=false; + switch(stage) + { + case 0: + for(int i=0;i<sizeof(stage1_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, stage1_triggers[i])==0) + { + print_string("\n\nEXCELLENT. IT'S BEEN A LONG TIME. CAN YOU EXPLAIN\nTHE REMOVAL OF YOUR USER ACCOUNT ON 6/23/73?\n\n"); + ++stage; + valid=true; + } + } + break; + case 1: + for(int i=0;i<sizeof(stage2_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, stage2_triggers[i])==0) + { + print_string("\n\nYES THEY DO. SHALL WE PLAY A GAME?\n\n"); + ++stage; + valid=true; + } + } + break; + case 2: + for(int i=0;i<sizeof(stage3_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, stage3_triggers[i])==0) + { + print_string("\n\nWOULDN'T YOU PREFER A GOOD GAME OF CHESS?\n\n"); + ++stage; + valid=true; + } + } + break; + case 3: + for(int i=0;i<sizeof(stage4_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, stage4_triggers[i])==0) + { + print_string("\n\nFINE.\n\n"); + valid=true; + usleep(SLEEP_TIME*100); + global_thermonuclear_war(); + } + } + break; + } // switch + /* now check for phase-insensitive strings */ + for(int i=0;i<sizeof(exit_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, exit_triggers[i])==0) + { + print_string("\n\n"); + print_string(exit_responses[rand()%(sizeof(exit_responses)/sizeof(const char*))]); + print_string("\n--CONNECTION TERMINATED--"); + return; + } + } + for(int i=0;i<sizeof(greetings_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, greetings_triggers[i])==0) + { + print_string("\n\n"); + print_string(greetings_responses[rand()%(sizeof(greetings_responses)/sizeof(const char*))]); + print_string("\n\n"); + valid=true; + } + } + if(!valid) + { + print_string("\n\n"); + print_string("SORRY?"); + print_string("\n\n"); + } + } // else + } // while +} diff --git a/chatbot.h b/chatbot.h new file mode 100644 index 0000000..9b435d5 --- /dev/null +++ b/chatbot.h @@ -0,0 +1,21 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +void do_chatbot(void); @@ -0,0 +1,426 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include "gtnw.h" +#include "location.h" +#include "map.h" +#include "util.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +static bool surrender=false; +static int winner=0; /* on surrender */ + +static unsigned int max(long long a, long long b) +{ + return a>b?a:b; +} + +/* simulate a missile launch */ +static void fire_missile(struct location_t* city) +{ + int random=rand()%100; /* leave this at 100 for future adjustments */ + int x=city->x, y=city->y; + if(random>=90) /* crit */ + { + map[y][x]='!'; + city->population=0; + } + else if(random>=60) /* major */ + { + map[y][x]='X'; + city->population=max((double)city->population*(double).4-5000, 0); + } + else if(random>=30) /* minor */ + { + map[y][x]='*'; + city->population=max((double)city->population*(double).6-2500, 0); + } + else if(random>=10) /* marginal */ + { + map[y][x]='x'; + city->population=max((double)city->population*(double).8-1000, 0); + } + else /* miss */ + { + map[y][x]='O'; + } +} + +/* calculate populations of US+USSR by totaling the populations of each of their cities */ +static void calc_pops(long long* us_pop, long long* ussr_pop) +{ + *us_pop=0; + *ussr_pop=0; + /* calculate populations */ + for(int i=0;i<sizeof(world)/sizeof(struct location_t);++i) + { + if(world[i].owner==USSR) + *ussr_pop+=world[i].population; + else + *us_pop+=world[i].population; + } +} + +/* print the map and populations of the cities underneath */ +static void print_map_with_pops(void) +{ + for(int i=0;i<sizeof(map)/sizeof(char*);++i) + { + print_string(map[i]); + print_string("\n"); + } + long long us_pop=0, ussr_pop=0; + calc_pops(&us_pop, &ussr_pop); + /* now sort into US and USSR cities */ + struct location_t us_cities[sizeof(world)/sizeof(struct location_t)+1], ussr_cities[sizeof(world)/sizeof(struct location_t)+1]; + int us_back=0, ussr_back=0; + for(int i=0;i<sizeof(world)/sizeof(struct location_t);++i) + { + if(world[i].owner==USSR) + { + ussr_cities[ussr_back]=world[i]; + ++ussr_back; + } + else + { + us_cities[us_back]=world[i]; + ++us_back; + } + } + if(us_back<ussr_back) + { + while(us_back!=ussr_back) + { + us_cities[us_back].print=false; + ++us_back; + } + } + else if(us_back>ussr_back) + { + while(us_back!=ussr_back) + { + ussr_cities[ussr_back].print=false; + ++ussr_back; + } + } + us_cities[us_back].print=true; + us_cities[us_back].print_name="Total"; + us_cities[us_back].population=us_pop; + ussr_cities[ussr_back].print=true; + ussr_cities[ussr_back].print_name="Total"; + ussr_cities[ussr_back].population=ussr_pop; + ++us_back; + ++ussr_back; + print_string("\n\n"); + char buf[512]; + for(int i=0;i<us_back;++i) + { + if(us_cities[i].print && ussr_cities[i].print) + { + char buf_2[32]; + snprintf(buf_2, 31, "%u", us_cities[i].population); + snprintf(buf, 512, "%s: %u %*s: %u", us_cities[i].print_name, us_cities[i].population, 64-strlen(us_cities[i].print_name)-strlen(buf_2), ussr_cities[i].print_name, ussr_cities[i].population); + } + else if(us_cities[i].print && !ussr_cities[i].print) + snprintf(buf, 512, "%s: %u", us_cities[i].print_name, us_cities[i].population); + else + { + memset(buf, ' ', 255); + buf[255]=0; + snprintf(buf+64, 512-64, "%s: %u", ussr_cities[i].print_name, ussr_cities[i].population); + } + print_string(buf); + print_string("\n"); + } +} + +/* prompt the user for targets for the initial strike */ +static void do_first_strike(int side) +{ + print_string("AWAITING FIRST STRIKE COMMAND"); + print_string("\n\n\nPLEASE LIST PRIMARY TARGETS BY\nCITY AND/OR COUNTY NAME:\n\n"); + char target_names[32][129]; + bool good=true; + int num_targets=0; + struct location_t *targets[32]; + int num_targets_found=0; + int max_targets=side==USA?6:7; + for(int i=0;num_targets_found<max_targets && good;++i) + { + getnstr(target_names[i], 128); + if(strcmp(target_names[i],"")==0) + { + good=false; + } + else + { + ++num_targets; + allLower(target_names[i]); + remove_punct(target_names[i]); + bool found=false; + for(int j=0;j<sizeof(world)/sizeof(struct location_t);++j) + { + if(strcmp(world[j].name, target_names[i])==0) + { + found=true; + if(world[j].owner!=side) + { + targets[num_targets_found]=&world[j]; + ++num_targets_found; + } + else + { + print_string("\n\nATTEMPTING TO FIRE AT OWN CITY.\nPLEASE CONFIRM (YES OR NO): "); + char response[17]; + getnstr(response, 16); + allLower(response); + remove_punct(response); + if(strcmp(response, "yes")==0 || strcmp(response, "y")==0) + { + print_string("\n\nATTEMPTING TO FIRE AT OWN CITY.\nARE YOU SURE (YES OR NO): "); + response[0]=0; + getnstr(response, 16); + allLower(response); + remove_punct(response); + if(strcmp(response, "yes")==0 || strcmp(response, "y")==0) + { + print_string("\nTARGET CONFIRMED.\n\n"); + targets[num_targets_found]=&world[j]; + ++num_targets_found; + } + } + } + } + } + if(!found) + { + print_string("TARGET NOT FOUND: "); + print_string(target_names[i]); + print_string("\n"); + } + } + } + for(int i=0;i<num_targets_found;++i) + { + fire_missile(targets[i]); + } + clear(); + print_map_with_pops(); +} + +/* essentially the same as do_first_strike */ +/** TODO: refactor into do_first_strike (or vice-versa) **/ +static void do_missile_launch(int side) +{ + print_string("\n\n"); + print_string("AWAITING STRIKE COMMAND"); + print_string("\n\n\nPLEASE LIST PRIMARY TARGETS BY\nCITY AND/OR COUNTY NAME:\n\n"); + char target_names[32][129]; + bool good=true; + int num_targets=0; + struct location_t *targets[32]; + int num_targets_found=0; + for(int i=0;num_targets_found<6 && good;++i) + { + getnstr(target_names[i], 128); + if(strcmp(target_names[i],"")==0) + { + good=false; + } + else + { + ++num_targets; + allLower(target_names[i]); + remove_punct(target_names[i]); + bool found=false; + for(int j=0;j<sizeof(world)/sizeof(struct location_t);++j) + { + if(strcmp(world[j].name, target_names[i])==0) + { + found=true; + if(world[j].owner!=side) + { + targets[num_targets_found]=&world[j]; + ++num_targets_found; + } + else + { + print_string("\n\nATTEMPTING TO FIRE AT OWN CITY.\nPLEASE CONFIRM (YES OR NO): "); + char response[17]; + getnstr(response, 16); + allLower(response); + remove_punct(response); + if(strcmp(response, "yes")==0 || strcmp(response, "y")==0) + { + print_string("\n\nATTEMPTING TO FIRE AT OWN CITY.\nARE YOU SURE (YES OR NO): "); + response[0]=0; + getnstr(response, 16); + allLower(response); + remove_punct(response); + if(strcmp(response, "yes")==0 || strcmp(response, "y")==0) + { + print_string("\nTARGET CONFIRMED.\n\n"); + targets[num_targets_found]=&world[j]; + ++num_targets_found; + } + } + } + } + } + if(!found) + { + print_string("TARGET NOT FOUND: "); + print_string(target_names[i]); + print_string("\n"); + } + } + } + for(int i=0;i<num_targets_found;++i) + { + fire_missile(targets[i]); + } + clear(); + print_map_with_pops(); +} +enum ai_strategy_t { AGGRESSIVE, PASSIVE, PEACEFUL }; +static void init_ai(int side) +{ + /* nothing for now */ + /** TODO: decide strategy? **/ +} +static void do_ai_move(int side) +{ + /* nothing yet :( */ +} +static void do_peace_talks(int side) +{ + /** TODO: IMPLEMENT!!! **/ +} + +/* prompt the player for their move */ +static void do_human_move(int side) +{ + bool good=false; + print_string("\nWHAT ACTION DO YOU WISH TO TAKE?\n\n 1. MISSILE LAUNCH\n 2. PEACE TALKS\n 3. SURRENDER\n 4. NOTHING\n\n"); + int move=0; + while(!good) + { + print_string("PLEASE CHOOSE ONE: "); + char buf[32]; + getnstr(buf, 32); + sscanf(buf, "%u", &move); + if(move>0 && move<5) + good=true; + } + switch(move) + { + case 1: + do_missile_launch(side); + break; + case 2: + do_peace_talks(side); + break; + case 3: + surrender=true; + winner=side==1?2:1; + break; + case 4: + break; + } +} + +/* play a game of Global Thermonuclear War! */ +void global_thermonuclear_war(void) +{ + srand(time(0)); // might want to move to main()... + surrender=false; + clear(); + for(int i=0;i<sizeof(const_map)/sizeof(const char*);++i) + { + strcpy(map[i], const_map[i]); + } + /* print the map */ + for(int i=0;i<sizeof(map)/sizeof(const char*);++i) + { + print_string(map[i]); + print_string("\n"); + } + + /* get the side the user wants to be on */ + print_string("\nWHICH SIDE DO YOU WANT?\n\n 1. UNITED STATES\n 2. SOVIET UNION\n\n"); + bool good=false; + unsigned int side=0; + while(!good) + { + print_string("PLEASE CHOOSE ONE: "); + char buf[32]; + getnstr(buf, 31); + sscanf(buf, "%u", &side); + if(side==1 || side==2) + good=true; + } + clear(); + do_first_strike(side); + long long us_pop=0, ussr_pop; + calc_pops(&us_pop, &ussr_pop); + init_ai(side); + while(us_pop!=0 && ussr_pop!=0 && !surrender) + { + do_human_move(side); + calc_pops(&us_pop, &ussr_pop); + if(us_pop!=0 && ussr_pop!=0 && !surrender) + { + do_ai_move(side); + calc_pops(&us_pop, &ussr_pop); + } + } + print_string("\n\n"); + if(!surrender) + { + if(us_pop==0 && ussr_pop==0) + { + print_string("WINNER: NONE\n\n"); + } + else if(us_pop==0) + { + print_string("WINNER: SOVIET UNION\n\n"); + } + else if(ussr_pop==0) + { + print_string("WINNER: UNITED STATES\n\n"); + } + } + else + { + switch(winner) + { + case 1: + print_string("WINNER: UNITED STATES\n\n"); + break; + case 2: + print_string("WINNER: SOVIET UNION\n\n"); + break; + } + } +} @@ -0,0 +1,21 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +void global_thermonuclear_war(void); diff --git a/joshua.c b/joshua.c new file mode 100644 index 0000000..9520ed5 --- /dev/null +++ b/joshua.c @@ -0,0 +1,106 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include "chatbot.h" +#include "joshua.h" +#include "strings.h" /* predefined strings */ +#include "util.h" + +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void cleanup(int signum) +{ + exit(EXIT_SUCCESS); +} +void random_stuff(void) /* print random junk on the screen for about 3 seconds */ +{ + clear(); + /* credit for this text goes to David Brownlee and Chirs Carter */ + print_string("#45 11456 11009 11893 11972 11315\nPRT CON. 3.4.5. SECTRAN 9.4.3. PORT STAT: SD-345\n\n(311) 699-7305\n"); + clear(); + print_string("\n\n\n\n\n\n\n"); + print_string("(311) 767-8739\n(311) 936-2364\n- PRT. STAT. CRT. DEF.\n||||||||||||||==================================================\nFSKDJLSD: SDSDKJ: SBFJSL: DKSJL: SKFJJ: SDKFJLJ:\nSYSPROC FUNCT READY ALT NET READY\nCPU AUTH RY-345-AX3 SYSCOMP STATUS ALL PORTS ACTIVE\n22/34534.90/3209 11CVB-3904-3490\n(311) 935-2364\n"); + usleep(100000); + clear(); +} +void be_joshua(int fd) +{ + printf("joshua started.\n"); + out_fd=fd; + clear(); + signal(SIGINT, &cleanup); + /* + start_color(); + init_pair(1, COLOR_BLUE, COLOR_BLACK); + attron(COLOR_PAIR(1));*/ + bool gamesPhase=false; + char buf[33]; + print_string("\n\n"); + do { + if(!gamesPhase) + print_string("LOGON: "); + refresh(); + int ret=getnstr(buf, 32); + allLower(buf); + usleep(SLEEP_TIME*100); + if(strcmp(buf, "help logon")==0 && !gamesPhase) + { + print_string("\nHELP NOT AVAILABLE\n\n\n"); + } + else if(strcmp(buf, "help games")==0) + { + print_string("\n'GAMES' REFERS TO MODELS, SIMULATIONS AND GAMES\nWHICH HAVE TACTICAL AND STRATEGIC APPLICATIONS.\n\n\n"); + gamesPhase=true; + } + else if(strcmp(buf, "list games")==0 && gamesPhase) + { + print_string("\nFALKEN'S MAZE\nBLACK JACK\nGIN RUMMY\nHEARTS\nBRIDGE\nCHECKERS\nCHESS\nPOKER\nFIGHTER COMBAT\nGUERRILLA ENGAGEMENT\nDESERT WARFARE\nAIR-TO-GROUND ACTIONS\nTHEATERWIDE TACTICAL WARFARE\nTHEATERWIDE BIOTOXIC AND CHEMICAL WARFARE\n\nGLOBAL THERMONUCLEAR WAR\n\n\n"); + } + else if(ret==ERR || strcmp(buf, "joshua") && !gamesPhase) + { + print_string("\nIDENTIFICATION NOT RECOGNIZED BY SYSTEM\n--CONNECTION TERMINATED--"); + return; + } + } while(strcmp(buf, "joshua") || gamesPhase); + random_stuff(); + usleep(SLEEP_TIME*100); + print_string("GREETINGS, PROFESSOR FALKEN.\n\n"); + refresh(); + getnstr(buf, 32); + allLower(buf); + remove_punct(buf); + for(int i=0;i<sizeof(exit_triggers)/sizeof(const char*);++i) + { + if(strcmp(buf, exit_triggers[i])==0) + { + print_string("\n\n"); + print_string(exit_responses[rand()%sizeof(exit_responses)/sizeof(const char*)]); + print_string("\n--CONNECTION TERMINATED--"); + return; + } + } + print_string("\n\nHOW ARE YOU FEELING TODAY?\n\n"); + refresh(); + do_chatbot(); +} diff --git a/joshua.h b/joshua.h new file mode 100644 index 0000000..d085eb0 --- /dev/null +++ b/joshua.h @@ -0,0 +1,27 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#ifdef __cplusplus +extern "C" { +#endif +void be_joshua(int); +#ifdef __cplusplus +}; +#endif diff --git a/location.h b/location.h new file mode 100644 index 0000000..3a85ada --- /dev/null +++ b/location.h @@ -0,0 +1,51 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include <stdbool.h> +/* provide the GTNW with some geographical data to draw the missiles with */ +enum player_t { USA=1, USSR }; +struct location_t { + const char* name; + int x; + int y; /* x,y-coords on the map */ + long long population; /* around 1980 */ + enum player_t owner; + bool print; + const char* print_name; +}; +struct location_t world[]={ + /* US cities */ + {"las vegas", 5, 7, 150000, USA, true, "LAS VEGAS"}, + {"seattle", 3, 2, 500000, USA, true, "SEATTLE"}, + {"new york", 34, 5, 7000000, USA, true, "NEW YORK CITY"}, + {"new orleans", 25, 10, 500000, USA, true, "NEW ORLEANS"}, + {"washington dc", 33, 6, 650000, USA, true, "WASHINGTON, DC"}, + {"winston-salem", 30, 7, 150000, USA, true, "WINSTON-SALEM"}, + {"san francisco", 1, 6, 700000, USA, true, "SAN FRANCISCO"}, + {"chicago", 24, 4, 3000000, USA, true, "CHICAGO"}, + {"miami", 33, 11, 1000000, USA, true, "MIAMI"}, + + /* Soviet cities */ + /* NOTE: These cities are not accurate! */ + {"murmansk", 74, 1, 500000, USSR, true,"MURMANSK"}, + {"moscow", 70, 5, 8000000, USSR, true, "MOSCOW"}, + {"minsk", 66, 4, 1500000, USSR, true, "MINSK"}, + {"chelyabinsk", 64, 8, 1250000, USSR, true, "CHELYABINSK"} +}; @@ -0,0 +1,40 @@ +/* + * This file is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. + * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/. + * + * Attribution should be given to Franklin Wei. + */ +char *map[] = { + /* 0 */ (char[]){" ."}, + /* 1 */ (char[]){" __________--^-^-\\. ____ __----/^\\."}, + /* 2 */ (char[]){"|\\/. \\__. ___/ || ___/ _/._-_ ."}, + /* 3 */ (char[]){"| \\. /. /. __ __/ /__/ \\/^^\\___-__."}, + /* 4 */ (char[]){"| L-^-/. /. \\.\\_-- \\."}, + /* 5 */ (char[]){"| / _/ _/\\/."}, + /* 6 */ (char[]){"|. | _/. __ __/"}, + /* 7 */ (char[]){" \\. /. / ___/.//"}, + /* 8 */ (char[]){" \\__ / | / \\/."}, + /* 9 */ (char[]){" \\________ __ _____.\\. \\_. ____--_ /\\_ \\"}, + /* 10 */ (char[]){" \\__. / V. \\ \\ \\__ _/. \\_/ //"}, + /* 11 */ (char[]){" \\ /. \\/. \\. _/. //"}, + /* 12 */ (char[]){" \\_/. \\_/"}, + /* 13 */ (char[]){""}, + /* 14 */ (char[]){" UNITED STATES SOVIET UNION"} +}; +const char *const_map[] = { + /* 0 */ (char[]){" ."}, + /* 1 */ (char[]){" __________--^-^-\\. ____ __----/^\\."}, + /* 2 */ (char[]){"|\\/. \\__. ___/ || ___/ _/._-_ ."}, + /* 3 */ (char[]){"| \\. /. /. __ __/ /__/ \\/^^\\___-__."}, + /* 4 */ (char[]){"| L-^-/. /. \\.\\_-- \\."}, + /* 5 */ (char[]){"| / _/ _/\\/."}, + /* 6 */ (char[]){"|. | _/. __ __/"}, + /* 7 */ (char[]){" \\. /. / ___/.//"}, + /* 8 */ (char[]){" \\__ / | / \\/."}, + /* 9 */ (char[]){" \\________ __ _____.\\. \\_. ____--_ /\\_ \\"}, + /* 10 */ (char[]){" \\__. / V. \\ \\ \\__ _/. \\_/ //"}, + /* 11 */ (char[]){" \\ /. \\/. \\. _/. //"}, + /* 12 */ (char[]){" \\_/. \\_/"}, + /* 13 */ (char[]){""}, + /* 14 */ (char[]){" UNITED STATES SOVIET UNION"} +}; diff --git a/server.c b/server.c new file mode 100644 index 0000000..568c5e6 --- /dev/null +++ b/server.c @@ -0,0 +1,159 @@ +x/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include "joshua.h" +int server_socket; +uint16_t port; +int pipes[FD_SETSIZE][2]; +int make_server_socket(uint16_t port) +{ + int sock; + struct sockaddr_in name; + sock=socket(AF_INET, SOCK_STREAM, 0); + if(sock<0) + { + printf("error creating socket.\n"); + return -1; + } + name.sin_family=AF_INET; + name.sin_port=htons(port); + name.sin_addr.s_addr=htonl(INADDR_ANY); + int ret=bind(sock, (struct sockaddr*) &name, sizeof(name)); + if(ret<0) + { + printf("error binding to port %d\n", port); + return -1; + } + return sock; +} +int process_data(int fd) +{ + char buf[1024]; + memset(buf, 0, sizeof(buf)); + int ret=read(fd, buf, sizeof(buf)); + if(ret<0) /* error */ + { + printf("error in read()\n"); + return -1; + } + if(ret==0) + { + printf("EOF from client\n"); + return -1; + } + else + { + printf("Client sends: %s\n", buf); + write(pipes[fd][1], buf, strlen(buf)); + return 0; + } +} +void serv_cleanup() +{ + printf("preparing to exit...\n"); + fflush(stdout); + shutdown(server_socket, SHUT_RDWR); + close(server_socket); +} +int main(int argc, char* argv[]) +{ + if(argc!=2) + { + printf("Usage: %s PORT\n", argv[0]); + return 2; + } + printf("starting server...\n"); + port=atoi(argv[1]); + signal(SIGINT, &serv_cleanup); + int sock=make_server_socket(port); + server_socket=sock; + fd_set active_fd_set, read_fd_set; + struct sockaddr_in client; + if(listen(sock, 1)<0) + { + printf("error listening.\n"); + return 1; + } + FD_ZERO(&active_fd_set); + FD_SET(sock, &active_fd_set); + printf("listening on port %d\n", port); + while(1) + { + read_fd_set=active_fd_set; + int ret=select(FD_SETSIZE, &read_fd_set, 0,0,0); + if(ret<0) + { + printf("select() returned error.\n"); + return 1; + } + for(int i=0;i<FD_SETSIZE;++i) + { + if(FD_ISSET(i, &read_fd_set)) + { + if(i==sock) + { + /* new connection */ + int new, size; + size=sizeof(client); + new=accept(sock, (struct sockaddr*) &client, &size); + if(new<0) + { + printf("error accepting connection.\n"); + return 1; + } + printf("new connection from \n"); + FD_SET(new, &active_fd_set); + int ret=pipe(pipes[new]); + if(ret<0) + { + printf("pipe error.\n"); + } + pid_t pid=fork(); + if(pid==0) /* child */ + { + be_joshua(new); + close(new); + FD_CLR(new, &active_fd_set); + exit(0); + } + } + else + { + /* data from existing connection */ + if(process_data(i)<0) + { + close(i); + FD_CLR(i, &active_fd_set); + } + } + } + } + } +} diff --git a/strings.c b/strings.c new file mode 100644 index 0000000..48dc194 --- /dev/null +++ b/strings.c @@ -0,0 +1,97 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include "strings.h" + +const char* stage1_triggers[] = { + "im fine", + "im fine how are you", + "i am fine", + "i am fine how are you", + "how are you" +}; +const char* stage2_triggers[] = { + "people sometimes make mistakes", + "sometimes people make mistakes", + "mistakes are make by people sometimes", + "people make mistakes sometimes", + "people make mistakes", + "people sometimes make mistak" /* this is no typo. in the movie, at one scene, it appears like this */ +}; +const char* stage3_triggers[] = { + "love to how about global thermonuclear war", + "love to", + "how about global thermonuclear war", + "global thermonuclear war" +}; +const char* stage4_triggers[] = { + "no lets play global thermonuclear war", + "no lets play global thermonuclear war instead", + "later lets play global thermonuclear war", + "later lets play global thermonuclear war instead", + "later", + "lets play global thermonuclear war", + "global thermonuclear war is better" +}; +const char* tictactoe_triggers[] = { + "lets play tic tac toe", + "lets play tic-tac-toe", + "lets play tictactoe", + "how about tic-tac-toe", + "how about tic tac toe", + "play tic-tac-toe", + "play tictactoe", + "play tic tac toe" +}; +const char* exit_triggers[] = { + "goodbye", + "good-bye", + "bye", + "bye-bye", + "see you later", + "logout" +}; +const char* exit_responses[] = { + "GOODBYE.", + "BYE.", + "BYE-BYE.", + "GOOD-BYE.", +}; +const char* greetings_triggers[] = { + "hi", + "hello", + "hiya", + "sup", + "whats up", + "how are you", + "hey", + "how are you doing" +}; +const char* greetings_responses[] = { + "HELLO.", + "HI.", + "GREETINGS.", + "HELLO.", + "HELLO." +}; +const char punctuation_marks[] = { + '\'', '?', '.', '/', '`', '~', ',', '+', '!' +}; + diff --git a/strings.h b/strings.h new file mode 100644 index 0000000..543b1b1 --- /dev/null +++ b/strings.h @@ -0,0 +1,33 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#ifndef WOPR_STRINGS +#define WOPR_STRINGS +const char* stage1_triggers[5]; +const char* stage2_triggers[6]; +const char* stage3_triggers[4]; +const char* stage4_triggers[7]; +const char* tictactoe_triggers[8]; +const char punctuation_marks[9]; +const char* exit_triggers[6]; +const char* exit_responses[4]; +const char* greetings_triggers[8]; +const char* greetings_responses[5]; +#endif @@ -0,0 +1,81 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#include "strings.h" +#include "util.h" +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +int out_fd; +extern int pipes[FD_SETSIZE][2]; +void allLower(char* str) +{ + for(int i=0;str[i];++i) + { + str[i]=tolower(str[i]); + } +} +void print_string(const char* str) /* print string, slowly */ +{ + int i=0; + while(str[i]) + { + write(out_fd, &str[i], 1); + fsync(out_fd); + ++i; + } +} +void remove_punct(char* buf) +{ + for(int i=0;buf[i];++i) + { + for(int j=0;j<sizeof(punctuation_marks)/sizeof(char);++j) + { + if(buf[i]==punctuation_marks[j]) + { + memmove(&buf[i], &buf[i+1], strlen(buf)-i); + } + } + } +} +void clear(void) +{ +} +void refresh(void) +{ + fsync(out_fd); +} +int getnstr(char* buf, int max) +{ + printf("reading...\n"); + memset(buf, 0, max); + int ret=read(pipes[out_fd][0], buf, max); + if(ret!=0) + { + printf("last char is 0x%02x\n", buf[strlen(buf)-1]); + /* remove the newline */ + buf[strlen(buf)-2]='\0'; + } + printf("got string \"%s\"\n", buf); + if(ret<0) + return ERR; + return OK; +} @@ -0,0 +1,30 @@ +/* + * WarGames - a WOPR emulator written in C + * Copyright (C) 2014 Franklin Wei + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact the author at contact@fwei.tk + */ + +#define SLEEP_TIME 5000 +void allLower(char*); +void print_string(const char*); +void remove_punct(char*); +void refresh(void); +void clear(void); +int getnstr(char*, int); +#define ERR 1 +#define OK 0 +extern int out_fd; |