Last active
August 29, 2015 14:00
-
-
Save skeeto/11222269 to your computer and use it in GitHub Desktop.
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
*.o | |
rpsls | |
*.db |
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
#include <ctime> | |
#include <cstring> | |
#include <stdexcept> | |
#include <sqlite3.h> | |
#include "db.hh" | |
const char *sql_timeout = "PRAGMA busy_timeout = 30000"; | |
const char *sql_table = "CREATE TABLE IF NOT EXISTS plays (time, host, choice)"; | |
const char *sql_insert = "INSERT INTO plays VALUES (?, ?, ?)"; | |
const char *sql_count = "SELECT choice, count(*) FROM plays GROUP BY choice"; | |
void DB::prepare(const char *sql, sqlite3_stmt **stmt) { | |
if (sqlite3_prepare_v2(db_, sql, std::strlen(sql), stmt, NULL) != SQLITE_OK) { | |
throw std::runtime_error{sqlite3_errmsg(db_)}; | |
} | |
} | |
int DB::step(sqlite3_stmt *stmt) { | |
int result = sqlite3_step(stmt); | |
if (result != SQLITE_ROW && result != SQLITE_DONE) { | |
throw std::runtime_error{sqlite3_errmsg(db_)}; | |
} | |
return result; | |
} | |
void DB::reset(sqlite3_stmt *stmt) { | |
if (sqlite3_reset(stmt) != SQLITE_OK) { | |
throw std::runtime_error{sqlite3_errmsg(db_)}; | |
} | |
} | |
void DB::finalize(sqlite3_stmt *stmt) { | |
if (sqlite3_finalize(stmt) != SQLITE_OK) { | |
throw std::runtime_error{sqlite3_errmsg(db_)}; | |
} | |
} | |
DB::~DB() { sqlite3_close(db_); } | |
DB::DB(const char *file) { | |
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; | |
if (sqlite3_open_v2(file, &db_, flags, nullptr) != SQLITE_OK) { | |
throw std::runtime_error{sqlite3_errmsg(db_)}; | |
} | |
sqlite3_stmt *stmt_table, *stmt_timeout; | |
prepare(sql_timeout, &stmt_timeout); | |
step(stmt_timeout); | |
finalize(stmt_timeout); | |
prepare(sql_table, &stmt_table); | |
step(stmt_table); | |
finalize(stmt_table); | |
prepare(sql_insert, &stmt_insert); | |
prepare(sql_count, &stmt_count); | |
} | |
void DB::event(const char *hostname, int choice) { | |
sqlite3_bind_int64(stmt_insert, 1, time(nullptr)); | |
sqlite3_bind_text(stmt_insert, 2, hostname, strlen(hostname), SQLITE_STATIC); | |
sqlite3_bind_int(stmt_insert, 3, choice); | |
step(stmt_insert); | |
reset(stmt_insert); | |
} | |
std::vector<int64_t> DB::counts() { | |
std::vector<int64_t> counts{}; | |
counts.resize(5); | |
while (step(stmt_count) == SQLITE_ROW) { | |
int choice = sqlite3_column_int(stmt_count, 0); | |
int64_t count = sqlite3_column_int64(stmt_count, 1); | |
counts[choice - 1] = count; | |
} | |
reset(stmt_count); | |
return std::move(counts); | |
} | |
int64_t DB::total() { | |
auto totals = counts(); | |
int64_t total = 0; | |
for (auto &i : totals) total += i; | |
return total; | |
} |
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
#ifndef RPSLS_DB_HH | |
#define RPSLS_DB_HH | |
#include <vector> | |
struct sqlite3; | |
struct sqlite3_stmt; | |
class DB { | |
public: | |
DB(const char *file); | |
~DB(); | |
void event(const char *hostname, int choice); | |
std::vector<int64_t> counts(); | |
int64_t total(); | |
private: | |
void prepare(const char *, sqlite3_stmt **); | |
int step(sqlite3_stmt *); | |
void reset(sqlite3_stmt *); | |
void finalize(sqlite3_stmt *); | |
sqlite3 *db_; | |
sqlite3_stmt *stmt_insert, *stmt_count; | |
}; | |
#endif |
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
CXX = clang++ | |
CXXFLAGS = -std=c++11 -Wall | |
LDLIBS = -lstdc++ -lsqlite3 | |
rpsls : rpsls.o db.o player.o | |
.PHONY : clean run | |
clean : | |
$(RM) *.o rpsls | |
run : rpsls | |
./$^ |
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
#include <iostream> | |
#include "db.hh" | |
#include "rpsls.hh" | |
#include "player.hh" | |
Choice Human::play() { | |
std::cout << "1. Rock" << std::endl; | |
std::cout << "2. Paper" << std::endl; | |
std::cout << "3. Scissors" << std::endl; | |
std::cout << "4. Lizard" << std::endl; | |
std::cout << "5. Spock" << std::endl; | |
std::cout << "(Any other input quits)" << std::endl; | |
std::cout << "Choice (1-5)? "; | |
int input; | |
if (!(std::cin >> input)) input = 0; | |
if (input < 0 || input > 5) input = 0; | |
choice_ = static_cast<Choice>(input); | |
std::cout << std::endl; | |
if (input != 0) { | |
db_->event(hostname_, input); | |
} | |
return choice_; | |
} | |
#define BOLD "\033[33m\033[1m" | |
#define UNBOLD "\033[22m\033[39m" | |
void Human::outcome(Choice other) { | |
std::cout << "You choose " << choice_ << "." << std::endl; | |
std::cout << "Other player chooses " << other << "." << std::endl; | |
if (other == choice_) { | |
std::cout << BOLD "Tie!" UNBOLD << std::endl << std::endl; | |
ties_++; | |
return; | |
} | |
const char *result = can_defeat(choice_, other); | |
if (result) { | |
std::cout << BOLD "You win" UNBOLD ": " | |
<< ": " << choice_ << " " << result << " " << other << std::endl; | |
wins_++; | |
} else { | |
result = can_defeat(other, choice_); | |
std::cout << BOLD "You lose" UNBOLD ": " | |
<< ": " << other << " " << result << " " << choice_ << std::endl; | |
losses_++; | |
} | |
std::cout << "W/T/L: " << wins_ << "/" << ties_ << "/" << losses_ | |
<< std::endl << std::endl; | |
} | |
Choice SmartAI::play() { | |
auto counts = db_->counts(); | |
int64_t total = 0; | |
for (auto &i : counts) total += i; | |
/* Guess other player's move, weighted by history. */ | |
Choice expected; | |
int64_t selection = rand() % total; | |
for (int i = 0; i < 5; i++) { | |
if (selection < counts[i]) { | |
expected = static_cast<Choice>(i + 1); | |
} else { | |
selection -= counts[i]; | |
} | |
} | |
/* Choice a random countermove. */ | |
Choice counter[2]; | |
int i = 0; | |
for (auto &outcome : outcomes) { | |
if (outcome.victim == expected) { | |
counter[i++] = outcome.attacker; | |
} | |
} | |
return counter[rand() % 2]; | |
} |
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
#ifndef RPSLS_PLAYER_HH | |
#define RPSLS_PLAYER_HH | |
#include <cstdlib> | |
#include "db.hh" | |
#include "rpsls.hh" | |
class Player { | |
public: | |
virtual Choice play() = 0; | |
virtual void outcome(Choice other) = 0; | |
}; | |
class Human : public Player { | |
public: | |
Human(DB *db, const char *hostname) : db_{db}, hostname_{hostname} {} | |
Choice play(); | |
void outcome(Choice other); | |
private: | |
DB *db_; | |
Choice choice_; | |
const char *hostname_; | |
int wins_ = 0, losses_ = 0, ties_ = 0; | |
}; | |
class RandomAI : public Player { | |
public: | |
Choice play() { return static_cast<Choice>(1 + (rand() % 5)); } | |
void outcome(Choice other) {}; | |
}; | |
class SmartAI : public Player { | |
public: | |
SmartAI(DB *db) : db_{db} {} | |
Choice play(); | |
void outcome(Choice other) {}; | |
private: | |
DB *db_; | |
}; | |
#endif |
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
#include <iostream> | |
#include <unistd.h> | |
#include "db.hh" | |
#include "rpsls.hh" | |
#include "player.hh" | |
const struct outcome outcomes[] = { | |
{Choice::SPOCK, "smashes", Choice::SCISSORS}, | |
{Choice::SPOCK, "vaporizes", Choice::ROCK}, | |
{Choice::SCISSORS, "cuts", Choice::PAPER}, | |
{Choice::SCISSORS, "decapitates", Choice::LIZARD}, | |
{Choice::PAPER, "covers", Choice::ROCK}, | |
{Choice::PAPER, "disproves", Choice::SPOCK}, | |
{Choice::ROCK, "crushes", Choice::LIZARD}, | |
{Choice::ROCK, "crushes", Choice::SCISSORS}, | |
{Choice::LIZARD, "eats", Choice::PAPER}, | |
{Choice::LIZARD, "poisons", Choice::SPOCK}}; | |
const char *can_defeat(Choice attacker, Choice victim) { | |
for (auto &outcome : outcomes) { | |
if (outcome.attacker == attacker && outcome.victim == victim) { | |
return outcome.action; | |
} | |
} | |
return nullptr; | |
} | |
std::ostream &operator<<(std::ostream &out, const Choice c) { | |
const char *names[] = {"QUIT", "rock", "paper", | |
"scissors", "lizard", "spock"}; | |
out << "\033[1m\033[4m" << names[static_cast<int>(c)] << "\033[24m\033[22m"; | |
return out; | |
} | |
bool play(Player &a, Player &b) { | |
Choice a_play, b_play; | |
do { | |
a_play = a.play(); | |
b_play = b.play(); | |
if (a_play == Choice::QUIT || b_play == Choice::QUIT) return false; | |
a.outcome(b_play); | |
b.outcome(a_play); | |
} while (a_play == b_play); | |
return true; | |
} | |
int main(int argc, char **argv) { | |
const char *hostname = "localhost", *dbfile = "rpsls.db"; | |
int opt; | |
while ((opt = getopt(argc, argv, "d:h:p")) != -1) { | |
switch (opt) { | |
case 'd': | |
dbfile = optarg; | |
break; | |
case 'h': | |
hostname = optarg; | |
break; | |
} | |
} | |
DB db{dbfile}; | |
std::cout << "Total games played: " << db.total() << std::endl << std::endl; | |
Human human{&db, hostname}; | |
SmartAI ai{&db}; | |
while (play(human, ai)) | |
; | |
return 0; | |
} |
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
#ifndef RPSLS_RPSLS_HH | |
#define RPSLS_RPSLS_HH | |
enum class Choice { QUIT = 0, ROCK, PAPER, SCISSORS, LIZARD, SPOCK }; | |
struct outcome { | |
Choice attacker; | |
const char *action; | |
Choice victim; | |
}; | |
extern const struct outcome outcomes[10]; | |
const char *can_defeat(Choice attacker, Choice victim); | |
std::ostream &operator<<(std::ostream &, const Choice); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment