Skip to content

Instantly share code, notes, and snippets.

@yuryu
Created January 23, 2015 05:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yuryu/f90088757f86d0a95f49 to your computer and use it in GitHub Desktop.
Save yuryu/f90088757f86d0a95f49 to your computer and use it in GitHub Desktop.
Kuji program
#include <iostream>
#include <algorithm>
#include <random>
#include <vector>
#include <fstream>
#include <set>
#include <unordered_set>
#include <string>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <getopt.h>
class Kuji {
public:
Kuji(): min_(INT_MIN), max_(INT_MAX) {};
Kuji(int min, int max, int seed): min_(min), max_(max), mt_(seed) {};
int pick();
void save(const std::string &path);
void load(const std::string &path);
const std::vector<int>& values() const;
int min() const { return min_; }
int max() const { return max_; }
private:
std::unordered_set<int> past_values_;
std::vector<int> past_values_ordered_;
std::vector<int> remaining_;
int min_;
int max_;
std::mt19937 mt_;
};
int Kuji::pick() {
if ( past_values_.size() >= (max_ - min_ + 1) ) return -1;
std::uniform_int_distribution<int> random(min_, max_);
int i;
do {
i = random(mt_);
} while ( past_values_.find(i) != past_values_.end() );
past_values_.insert(i);
past_values_ordered_.push_back(i);
return i;
}
void Kuji::save(const std::string &path) {
std::ofstream ofs(path);
ofs << min_ << ' ' << max_ << std::endl;
ofs << mt_ << std::endl;
std::ostream_iterator<int> out_iter(ofs, " ");
std::copy(past_values_ordered_.begin(), past_values_ordered_.end(), out_iter);
}
void Kuji::load(const std::string &path) {
std::ifstream ifs(path);
ifs >> min_ >> max_ >> mt_;
past_values_.clear();
int n;
while ( ifs >> n ) {
past_values_.insert(n);
past_values_ordered_.push_back(n);
}
}
const std::vector<int>& Kuji::values() const {
return past_values_ordered_;
}
void usage() {
fputs(
"Usage: kuji [options] command\n"
"\n"
"Commands:\n"
" init: Start new kuji\n"
" pick: Pick up one kuji\n"
"Options:\n"
" --path=path (-p) file to save/load state\n"
" --seed=seed (-s) random seed for new kuji\n"
" --min=min (-m) minimum value for new kuji\n"
" --max=max (-m) maximum value for new kuji\n"
" --help (-h) show this help\n"
, stderr);
}
static int opt_parse_int(const char *opt, const char *arg) {
char *endptr;
const int i = std::strtol(arg, &endptr, 0);
if ( *endptr != '\0' ) {
fprintf(stderr, "Option %s requires integer\n", opt);
usage();
std::exit(1);
}
return i;
}
static int num_places (int n) {
int r = 1;
if (n < 0) n = (n == INT_MIN) ? INT_MAX : -n;
while (n > 9) {
n /= 10;
r++;
}
return r;
}
static void print_current_values(const Kuji &kuji) {
const auto values = kuji.values();
const int values_per_line = 10;
int count = 0;
const int digits = num_places(kuji.max());
for ( int v: values ) {
if ( count++ % values_per_line == 0 ) {
printf("\n%*d", digits, v);
} else {
printf(", %*d", digits, v);
}
}
putchar('\n');
}
int main(int argc, char *argv[]) {
static struct option longopts[] = {
{ "path", required_argument, nullptr, 'p' },
{ "seed", required_argument, nullptr, 's' },
{ "min", required_argument, nullptr, 'm' },
{ "max", required_argument, nullptr, 'n' },
{ "help", no_argument, nullptr, 'h' },
{ nullptr, 0, nullptr, 0 },
};
const char *path = nullptr;
int min = INT_MAX, max = INT_MIN;
int seed = static_cast<int>(std::time(nullptr));
int ch;
int optindex;
while ( (ch = getopt_long(argc, argv, "p:s:m:n:", longopts, &optindex)) != -1 ) {
switch (ch) {
case 'p':
path = optarg;
break;
case 's':
seed = opt_parse_int(longopts[optindex].name, optarg);
break;
case 'm':
min = opt_parse_int(longopts[optindex].name, optarg);
break;
case 'n':
max = opt_parse_int(longopts[optindex].name, optarg);
break;
case 'h':
usage();
return 1;
default:
fprintf(stderr, "Invalid option: %c\n", ch);
usage();
return 1;
}
}
argc -= optind;
argv += optind;
if ( argc < 1 ) {
fputs("Command is required\n", stderr);
usage();
return 1;
}
if ( path == nullptr ) {
fputs("Path is required\n", stderr);
usage();
return 1;
}
std::string command = argv[0];
if ( command == "init" ) {
if ( min == INT_MAX || max == INT_MIN ) {
fputs("Specify min and max values\n", stderr);
usage();
return 1;
}
if ( min > max ) {
fputs("Minimum value must be less than or equal to maximum value\n", stderr);
usage();
return 1;
}
Kuji kuji(min, max, seed);
kuji.save(path);
} else if ( command == "pick" ) {
Kuji kuji;
kuji.load(path);
int i = kuji.pick();
if ( i == -1 ) {
puts("All kuji picked.");
} else {
kuji.save(path);
printf("Picked up number: %*d\n", num_places(kuji.max()), i);
}
fputs("Current numbers:", stdout);
print_current_values(kuji);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment