├── old ├── game │ ├── README │ ├── config.h.in │ ├── CMakeLists.txt │ ├── cntdn.c │ ├── TODO │ ├── cntdn.h │ ├── players.c │ ├── dictcorner.c │ ├── opts.c │ ├── strings.c │ └── games.c ├── config.h.in ├── numbers.h ├── gen-letter-sets.pl ├── make-annotated-dictionary ├── numbers_main.c ├── README ├── letters.h ├── CMakeLists.txt ├── gen-conundrum ├── tkletters.pl ├── gletters.py ├── ga │ └── ga-numbers ├── numbers.c ├── letters_main.c ├── letters.c └── lettersd.c ├── README ├── mk-js-dict ├── index.html ├── ui.js └── cntdn.js /old/game/README: -------------------------------------------------------------------------------- 1 | The game simulator never got finished, but is preserved here because the parts 2 | of it that were written do work. 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a javascript solver for the letters and numbers games. To use it, 2 | include dictionary.js and cntdn.js, and write an equivalent of ui.js. 3 | 4 | Online version available at http://incoherency.co.uk/countdown/ 5 | -------------------------------------------------------------------------------- /old/config.h.in: -------------------------------------------------------------------------------- 1 | /* Configuration for cntdn 2 | 3 | config.h is generated by cmake from config.h.in 4 | 5 | James Stanley 2010 */ 6 | 7 | #ifndef CONFIG_H_INC 8 | #define CONFIG_H_INC 9 | 10 | #cmakedefine DEFAULT_DICT "@DEFAULT_DICT@" 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /old/game/config.h.in: -------------------------------------------------------------------------------- 1 | /* Configuration for cntdn 2 | 3 | config.h is generated by cmake from config.h.in 4 | 5 | James Stanley 2010 */ 6 | 7 | #ifndef CONFIG_H_INC 8 | #define CONFIG_H_INC 9 | 10 | #cmakedefine DEFAULT_DICT "@DEFAULT_DICT@" 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /old/numbers.h: -------------------------------------------------------------------------------- 1 | /* Header for numbers.c 2 | 3 | James Stanley 2010 */ 4 | 5 | #ifndef NUMBERS_H_INC 6 | #define NUMBERS_H_INC 7 | 8 | #include 9 | #include 10 | 11 | extern int number[6]; 12 | extern int target; 13 | 14 | extern int numbers; 15 | 16 | int solve(void); 17 | void print_vals(void); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /old/gen-letter-sets.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Generate letter sets for countdown 3 | # James Stanley 2011 4 | 5 | use strict; 6 | use warnings; 7 | 8 | my @LETTERS = split( //, 'abcdefghijklmnopqrstuvwxyz' ); 9 | 10 | my $sets = shift or die "usage: gen-letter-sets N\n"; 11 | 12 | while ($sets--) { 13 | for (my $i = 0; $i < 9; $i++) { 14 | print $LETTERS[rand @LETTERS]; 15 | } 16 | print "\n"; 17 | } 18 | -------------------------------------------------------------------------------- /old/game/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(cntdn-game) 3 | 4 | add_executable(cntdn cntdn.c dictcorner.c games.c ../letters.c ../numbers.c opts.c players.c strings.c) 5 | 6 | set(CMAKE_C_FLAGS_DEBUG "-g -Wall") 7 | set(CMAKE_C_FLAGS_RELEASE "-O3") 8 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3") 9 | 10 | set(DEFAULT_DICT ${CMAKE_INSTALL_PREFIX}/share/dict/cntdn-dictionary) 11 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 12 | 13 | install(TARGETS cntdn 14 | RUNTIME DESTINATION bin) 15 | -------------------------------------------------------------------------------- /old/make-annotated-dictionary: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | my %count; 7 | 8 | open( my $fh1, '<', 'counted-words' ) 9 | or die "can't read counted-words: $!\n"; 10 | while (<$fh1>) { 11 | chomp; 12 | 13 | my ($w, $c) = split /\s+/; 14 | 15 | $count{$w} = $c; 16 | } 17 | 18 | open( my $fh2, '<', 'dictionary' ) 19 | or die "can't read dictionary: $!\n"; 20 | while (my $w = <$fh2>) { 21 | chomp $w; 22 | 23 | if ($count{$w}) { 24 | print "$w\t$count{$w}\n"; 25 | } else { 26 | print "$w\t1\n"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /old/numbers_main.c: -------------------------------------------------------------------------------- 1 | /* Main source file for numbers solver 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "numbers.h" 6 | 7 | int number[6]; 8 | int target; 9 | 10 | int main(int argc, char **argv) { 11 | int i; 12 | 13 | /* not enough arguments */ 14 | if(argc != 8) { 15 | fprintf(stderr, 16 | "Usage: %s target n1 n2 n3 n4 n5 n6\n" 17 | "Where n1..6 are the 6 numbers and target is the target number.\n", 18 | argv[0]); 19 | 20 | return 1; 21 | } 22 | 23 | /* read the arguments */ 24 | target = atoi(argv[1]); 25 | for(i = 0; i < 6; i++) { 26 | number[i] = atoi(argv[i + 2]); 27 | } 28 | numbers = 6; 29 | 30 | /* solve these numbers */ 31 | if(solve() == 1) print_vals(); 32 | else fprintf(stderr, "No solution.\n"); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /old/README: -------------------------------------------------------------------------------- 1 | This project includes the following programs: 2 | 3 | numbers - command-line solver for the numbers game 4 | letters - command-line solver for the letters game 5 | gletters - python gtk solver for the letters game 6 | tkletters - perl tk solver for the letters game 7 | 8 | There is also the game/ directory which contains a very silly and unfinished 9 | simulator for the countdown game, and js/ which contains an excellent 10 | javascript solver for the letters and numbers games. 11 | 12 | Build and install the solvers with: 13 | $ mkdir build && cd build 14 | $ cmake .. && make 15 | # make install 16 | 17 | Build and install the game simulator with: 18 | $ mkdir game/build && cd game/build 19 | $ cmake .. && make 20 | # make install 21 | 22 | The dictionary is 2of4brif.txt from 12dicts revision 5. 23 | http://wordlist.sourceforge.net/ 24 | -------------------------------------------------------------------------------- /old/letters.h: -------------------------------------------------------------------------------- 1 | /* Header for letters.c 2 | 3 | James Stanley 2010 */ 4 | 5 | #ifndef LETTERS_H_INC 6 | #define LETTERS_H_INC 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "config.h" 14 | 15 | #ifndef BUFLEN 16 | #define BUFLEN 8192 17 | #endif 18 | 19 | typedef struct TrieNode { 20 | struct TrieNode *child[26]; 21 | char end_word; 22 | } TrieNode; 23 | 24 | extern TrieNode *dictionary; 25 | 26 | extern int ignore_invalid; 27 | extern int minletters; 28 | 29 | void load_dictionary(const char *path, int maxlen); 30 | void solve_letters(const char *letters, 31 | void (*callback)(const char *word, void *data), void *data); 32 | int word_in_dictionary(const char *word); 33 | int can_make_word(const char *word, const char *letters); 34 | void die(const char *fmt, ...); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /old/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(cntdn) 3 | 4 | add_executable(letters letters.c letters_main.c) 5 | add_executable(lettersd letters.c lettersd.c) 6 | add_executable(numbers numbers.c numbers_main.c) 7 | 8 | target_link_libraries(lettersd pthread) 9 | 10 | set(CMAKE_C_FLAGS_DEBUG "-g -Wall") 11 | set(CMAKE_C_FLAGS_RELEASE "-O3") 12 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3") 13 | 14 | set(DEFAULT_DICT ${CMAKE_INSTALL_PREFIX}/share/dict/cntdn-dictionary) 15 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 16 | 17 | install(TARGETS letters lettersd numbers 18 | RUNTIME DESTINATION bin) 19 | install(PROGRAMS gletters.py RENAME gletters 20 | DESTINATION bin) 21 | install(PROGRAMS tkletters.pl RENAME tkletters 22 | DESTINATION bin) 23 | install(FILES dictionary RENAME cntdn-dictionary 24 | DESTINATION share/dict) 25 | -------------------------------------------------------------------------------- /old/gen-conundrum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # generate a conundrum 3 | # James Stanley 2014 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Text::Levenshtein qw(distance); 9 | 10 | my %words; 11 | my %have9; 12 | 13 | while (my $word = <>) { 14 | chomp $word; 15 | 16 | if (length $word < 9) { 17 | push @{ $words{length $word} }, $word; 18 | } elsif (length $word == 9) { 19 | push @{ $have9{join '', sort(split(//, $word))} }, $word; 20 | } 21 | } 22 | 23 | while (1) { 24 | my $w1 = $words{4}[rand @{ $words{4} } - 1]; 25 | my $w2 = $words{5}[rand @{ $words{5} } - 1]; 26 | 27 | next if substr($w1, -1, 1) eq substr($w2, 0, 1); 28 | my $w = "$w1$w2"; 29 | if ($have9{join '', sort(split(//, $w))}) { 30 | my @words; 31 | for my $word (@{ $have9{join '', sort(split(//, $w))} }) { 32 | push @words, $word if distance($word, "$w1$w2") > 4 && distance($word, "$w2$w1") > 4; 33 | } 34 | print "$w1$w2 => ", join(', ', @words), "\n" if @words; 35 | 36 | last if @words; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mk-js-dict: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Make a javascript trie representing a dictionary 3 | # James Stanley 2014 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use JSON; 9 | 10 | my $dictionary = {}; 11 | 12 | while (<>) { 13 | chomp; 14 | 15 | my ($w, $count) = split /\s+/; 16 | 17 | add_word($w => $count); 18 | } 19 | 20 | my $jsondict = encode_json($dictionary); 21 | $jsondict =~ s/"//g; 22 | my $x = '{0:1,s:{0:1}}'; 23 | $jsondict =~ s/\Q$x\E/x/g; 24 | my $y = '{i:{n:{g:{0:1}}}}'; 25 | $jsondict =~ s/\Q$y\E/y/g; 26 | my $z = '{0:1}'; 27 | $jsondict =~ s/\Q$z\E/z/g; 28 | 29 | my $morevars = ''; 30 | for my $l ('a'..'z') { 31 | my $u = uc $l; 32 | my $t = "{$l:z}"; 33 | $jsondict =~ s/\Q$t\E/$u/g; 34 | $morevars .= "var $u=$t;"; 35 | } 36 | 37 | print "var x=$x;var y=$y;var z=$z;${morevars}var dictionary=$jsondict;\n"; 38 | 39 | sub add_word { 40 | my ($word, $count) = @_; 41 | 42 | my $n = $dictionary; 43 | 44 | while (length $word) { 45 | # TODO: deal with invalid letters 46 | 47 | $word =~ s/^(.)//; 48 | my $c = $1; 49 | 50 | if (!$n->{$c}) { 51 | $n->{$c} = {}; 52 | } 53 | 54 | $n = $n->{$c}; 55 | } 56 | 57 | $n->{0} = $count ? $count : 1; 58 | } 59 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cntdn.js 5 | 6 | 7 |

Cntdn.js

8 | 9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /old/game/cntdn.c: -------------------------------------------------------------------------------- 1 | /* cntdn - countdown game simulator 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | /* print the reason on stderr and terminate the program */ 8 | void die(const char *fmt, ...) { 9 | va_list args; 10 | 11 | va_start(args, fmt); 12 | vfprintf(stderr, fmt, args); 13 | va_end(args); 14 | 15 | fputc('\n', stderr); 16 | 17 | exit(1); 18 | } 19 | 20 | /* return a pointer to a static buffer containing a line read from stdin */ 21 | char *get_line(void) { 22 | static char buf[BUFLEN]; 23 | char *p; 24 | 25 | if(!noflush) tcflush(STDIN_FILENO, TCIFLUSH); 26 | 27 | if(!fgets(buf, BUFLEN, stdin)) die("error: eof on stdin"); 28 | 29 | if((p = strchr(buf, '\n'))) *p = '\0'; 30 | 31 | return buf; 32 | } 33 | 34 | int main(int argc, char **argv) { 35 | char *p; 36 | 37 | srand(time(NULL)); 38 | 39 | parse_opts(argc, argv); 40 | 41 | load_dictionary(dictionary_path, num_letters); 42 | 43 | make_players(); 44 | 45 | for(p = format; *p; p++) { 46 | switch(tolower(*p)) { 47 | case 'l': letters_round(); show_scores(*(p+1) == '\0'); break; 48 | case 'n': numbers_round(); show_scores(*(p+1) == '\0'); break; 49 | case 'g': guest_chat(); break; 50 | case 't': teatime_teaser(); break; 51 | case 'o': origin_of_words(); break; 52 | case 'c': conundrum(); show_scores(*(p+1) == '\0'); break; 53 | default: 54 | fprintf(stderr, "warning: unknown round letter '%c', skipping round\n", 55 | *p); 56 | } 57 | } 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /old/game/TODO: -------------------------------------------------------------------------------- 1 | Things that need to happen: 2 | 3 | game format: 4 | four letter rounds 5 | number round 6 | celebrity chat 7 | teatime teaser 8 | four letter rounds 9 | origin of words 10 | number round 11 | teatime teaser 12 | three letter rounds 13 | number round 14 | conundrum 15 | 16 | other things to do: 17 | game logs 18 | when only one player, don't do some stuff like asking word length 19 | might not need player names so much if only one player 20 | make i/o more robust 21 | get a proper dictionary with definitions 22 | when both players get the same length who gives word first? 23 | 24 | numbers round: 25 | 24 numbers available 26 | 6 chosen (x large, y small) 27 | pick x and y before any numbers are revealed 28 | 4 large (25, 50, 75, 100) 29 | 20 small (2 each of 1..10) 30 | Player: "4 {large, from the top} and 2 {small, from the bottom} please, Rachel" 31 | target 100..999 32 | 30 second timer 33 | each player declares the answer they reached 34 | get best to describe solution 35 | "10 points are given for an exact answer, 7 points for a non-exact solution 36 | up to 5 from the target, and 5 points for a solution between 6 and 10 from 37 | the target. If neither contestant can get within 10, no points are awarded." 38 | 39 | letters round: 40 | 9 letters 41 | pick consonant or vowel, letter revealed, repeat 42 | at least 3 vowels and 4 consonants 43 | letter probabilities weighted to english text frequency 44 | 30 second timer 45 | each player declares number of letters 46 | get worst to say word, then best 47 | award n points to winner for n letters, or 18 points for 9 letters 48 | dictionary corner give their best word, with the celebrity claiming not to have 49 | found the word, and Susie gives a definition. 50 | 51 | conundrum: 52 | Jeff: "It's time to reveal today's... countdown conundrum." 53 | all 9 letters are revealed 54 | 30 second timer 55 | answer on buzzer 56 | Jeff: "Let's take a look..." 57 | answer flips over slowly 58 | 10 points to winner 59 | 60 | numbers solver: 61 | use linked list and Dancing Links (NOTE: did this and it turned out a little slower than array shuffling) 62 | -------------------------------------------------------------------------------- /old/game/cntdn.h: -------------------------------------------------------------------------------- 1 | /* Header for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #ifndef CNTDN_H_INC 6 | #define CNTDN_H_INC 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define BUFLEN 8192 19 | 20 | /* cntdn.c */ 21 | void die(const char *fmt, ...); 22 | char *get_line(void); 23 | 24 | /* opts.c */ 25 | extern char *program_name; 26 | 27 | extern int players; 28 | extern int timer; 29 | extern char *format; 30 | extern int nocolour; 31 | extern char *dictionary_path; 32 | extern int ignore_invalid; 33 | extern int noflush; 34 | extern int num_letters; 35 | 36 | void parse_opts(int argc, char **argv); 37 | 38 | /* players.c */ 39 | typedef struct { 40 | char *raw_name; 41 | char *name; 42 | int length; 43 | char *word; 44 | int answer; 45 | int score; 46 | } Player; 47 | extern Player *player; 48 | 49 | void make_players(void); 50 | void show_scores(int endgame); 51 | 52 | /* strings.c */ 53 | #define JEFF 0 54 | #define SUSIE 1 55 | #define RACHEL 2 56 | #define GUEST 3 57 | 58 | extern const char *number_colour; 59 | extern const char *letter_colour; 60 | extern const char *colour_off; 61 | extern const char *colour[]; 62 | extern const int num_colours; 63 | 64 | const char *get_name(void); 65 | void init_numbers(void); 66 | char get_vowel(void); 67 | char get_consonant(void); 68 | int get_small(void); 69 | int get_large(void); 70 | void colours_off(void); 71 | void speak(int presenter, const char *fmt, ...); 72 | void slow_printf(const char *fmt, ...); 73 | 74 | /* games.c */ 75 | extern int number[6]; 76 | extern int target; 77 | 78 | void letters_round(void); 79 | void numbers_round(void); 80 | void teatime_teaser(void); 81 | void conundrum(void); 82 | 83 | /* dictcorner.c */ 84 | void origin_of_words(void); 85 | void guest_chat(void); 86 | int valid_word(int p, const char *letters); 87 | void dict_solve(const char *letters); 88 | 89 | /* letters.c */ 90 | #include "../letters.h" 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /old/tkletters.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Tk interface to the cntdn letters solver 3 | # James Stanley 2011 4 | 5 | use Tk; 6 | use IPC::Open2; 7 | 8 | use strict; 9 | use warnings; 10 | 11 | my ($childin, $childout, $display); 12 | 13 | # Handle a key press by updating the display 14 | sub keypress { 15 | my ($input) = @_; 16 | 17 | my @words = (); 18 | 19 | # don't get any capital letters 20 | $input = lc $input; 21 | 22 | # give the letters to the solver 23 | print $childin "$input\n"; 24 | 25 | # read words from the solver, adding them to @words 26 | my $line = <$childout>; 27 | while ($line ne "\n") { 28 | push(@words, $line); 29 | 30 | $line = <$childout>; 31 | } 32 | 33 | # sort @words by length 34 | @words = sort { length( $b ) <=> length( $a ) } @words; 35 | 36 | # set the text in the display to be the words 37 | $display->configure( -state => 'normal' ); 38 | $display->delete( '1.0', 'end' ); 39 | $display->insert( '1.0', (join '', @words) ); 40 | $display->configure( -state => 'disabled' ); 41 | 42 | return 1; 43 | } 44 | 45 | # make a Tk window 46 | my $mw = MainWindow->new(); 47 | 48 | # make the letter input box 49 | my $input = $mw->Entry( -validate => 'key', -vcmd => \&keypress ); 50 | 51 | # make the scrollbar 52 | my $scrollbar = $mw->Scrollbar( -orient => 'vertical' ); 53 | 54 | # make the area displaying the solution words 55 | $display = $mw->Text( -width => 25, -height => 20, -state => 'disabled', 56 | -yscrollcommand => ['set' => $scrollbar] ); 57 | 58 | # make the scrollbar control the display 59 | $scrollbar->configure( -command => ['yview' => $display] ); 60 | 61 | # pack widgets in to window 62 | $input->pack( -fill => 'x' ); 63 | $scrollbar->pack( -side => 'right', -fill => 'y'); 64 | $display->pack( -expand => 1, -side => 'left', -fill => 'both' ); 65 | 66 | # spawn a letters game solver 67 | my $pid = open2( $childout, $childin, 'letters', '-I', '-m0' ); 68 | 69 | # start the Tk main loop 70 | MainLoop(); 71 | 72 | # let the solver exit 73 | close $childin; 74 | waitpid( $pid, 0 ); 75 | -------------------------------------------------------------------------------- /old/gletters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # GTK interface to countdown letters solver 3 | # James Stanley 2010 4 | 5 | import subprocess 6 | import sys 7 | 8 | import pygtk 9 | pygtk.require('2.0') 10 | import gtk 11 | import pango 12 | 13 | def lengthcmp(s1, s2): 14 | return len(s2) - len(s1) 15 | 16 | class GLetters: 17 | def delete_event(self, widget, event, data=None): 18 | return False 19 | 20 | def destroy(self, widget, data=None): 21 | gtk.main_quit() 22 | 23 | def solve_letters(self, widget, event, data=None): 24 | self.solver.stdin.write(self.inputbox.get_text().lower() + '\n') 25 | 26 | lines = [] 27 | 28 | while True: 29 | line = self.solver.stdout.readline() 30 | if line == '\n': 31 | break 32 | if line == '': 33 | sys.exit() 34 | lines.append(line) 35 | 36 | text = '' 37 | for line in sorted(lines, key = lambda s: -len(s)): 38 | text = text + line.capitalize() 39 | self.wordlist.set_text(text) 40 | 41 | def __init__(self): 42 | self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 43 | self.inputbox = gtk.Entry() 44 | self.wordlist = gtk.TextBuffer() 45 | 46 | self.window.set_title('gletters') 47 | self.window.connect('delete_event', self.delete_event) 48 | self.window.connect('destroy', self.destroy) 49 | self.window.resize(200, 300) 50 | self.window.set_position(gtk.WIN_POS_CENTER) 51 | 52 | self.inputbox.connect("key_release_event", self.solve_letters) 53 | 54 | textview = gtk.TextView() 55 | textview.set_buffer(self.wordlist) 56 | textview.set_editable(False) 57 | textview.modify_font(pango.FontDescription('Monospace')) 58 | 59 | box = gtk.VBox(False, 0) 60 | box.pack_start(self.inputbox, False, True, 0) 61 | sw = gtk.ScrolledWindow() 62 | sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 63 | sw.add(textview) 64 | box.pack_start(sw, True, True, 0) 65 | self.window.add(box) 66 | 67 | self.window.show_all() 68 | 69 | self.solver = subprocess.Popen(['letters', '-I', '-m0'], \ 70 | stdin=subprocess.PIPE, stdout=subprocess.PIPE) 71 | 72 | def main(self): 73 | gtk.main() 74 | 75 | if __name__ == '__main__': 76 | gletters = GLetters() 77 | gletters.main() 78 | -------------------------------------------------------------------------------- /old/game/players.c: -------------------------------------------------------------------------------- 1 | /* Player handling for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | Player *player; 8 | 9 | /* Read and assign name for player n */ 10 | static void read_name(int n) { 11 | char *line; 12 | int col_num = n % num_colours; 13 | size_t len; 14 | 15 | if(players == 1) printf("Name for player: %s", colour[col_num]); 16 | else printf("Name for player %d: %s", n + 1, colour[col_num]); 17 | 18 | line = get_line(); 19 | printf("%s", colour_off); 20 | 21 | if(*line == '\0') player[n].raw_name = strdup(get_name()); 22 | else player[n].raw_name = strdup(line); 23 | 24 | /* add colour to the printing name */ 25 | len = strlen(colour[col_num]) + strlen(player[n].raw_name) 26 | + strlen(colour_off) + 1; 27 | player[n].name = malloc(len); 28 | snprintf(player[n].name, len, "%s%s%s", colour[col_num], 29 | player[n].raw_name, colour_off); 30 | 31 | /* if a name wasn't supplied, inform the player of his name */ 32 | if(*line == '\0') 33 | printf("Player %d will be called %s.\n", n + 1, player[n].name); 34 | } 35 | 36 | /* compare player scores for qsort */ 37 | static int score_cmp(const void *one, const void *two) { 38 | const int *a = one, *b = two; 39 | 40 | return player[*b].score - player[*a].score; 41 | } 42 | 43 | /* Allocate player structures */ 44 | void make_players(void) { 45 | int i; 46 | 47 | player = malloc(sizeof(Player) * players); 48 | memset(player, 0, sizeof(Player) * players); 49 | 50 | for(i = 0; i < players; i++) read_name(i); 51 | } 52 | 53 | /* show player scores, if endgame is non-zero, it will say "player wins with 54 | N points" for the winning player */ 55 | void show_scores(int endgame) { 56 | int i; 57 | int *player_order; 58 | 59 | player_order = malloc(sizeof(int) * players); 60 | for(i = 0; i < players; i++) player_order[i] = i; 61 | 62 | qsort(player_order, players, sizeof(int), score_cmp); 63 | 64 | printf("\n"); 65 | 66 | for(i = 0; i < players; i++) { 67 | printf(" %s %s %d points.\n", player[player_order[i]].name, 68 | (endgame & 69 | (player[player_order[i]].score == player[player_order[0]].score) ? 70 | (players == 0 ? "finishes with" : "wins with") : "has"), 71 | player[player_order[i]].score); 72 | } 73 | 74 | printf("\n"); 75 | } 76 | -------------------------------------------------------------------------------- /old/game/dictcorner.c: -------------------------------------------------------------------------------- 1 | /* Dictionary corner for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | static char best_word[256]; 8 | 9 | /* Give a Susie Dent monologue on the origin of a word */ 10 | void origin_of_words(void) { 11 | printf(" *** Origin of words\n"); 12 | } 13 | 14 | /* The guest tells us a story */ 15 | void guest_chat(void) { 16 | printf(" *** Guest tells a story\n"); 17 | } 18 | 19 | /* Return 1 if player p's word is valid and 0 otherwise. This functions produces 20 | game output */ 21 | int valid_word(int p, const char *letters) { 22 | char *line; 23 | 24 | if(!can_make_word(player[p].word, letters)) { 25 | /* TODO: this should be more specific about what letters are missing */ 26 | speak(SUSIE, "Sorry %s, you can't make \"%s\" with those letters.", 27 | player[p].name, player[p].word); 28 | 29 | if(!word_in_dictionary(player[p].word)) 30 | slow_printf(" It isn't in my dictionary, either."); 31 | 32 | printf("\n"); 33 | return 0; 34 | } 35 | 36 | if(!word_in_dictionary(player[p].word)) { 37 | while(1) { 38 | speak(SUSIE, "%s, \"%s\" isn't in my dictionary. Do you want to use it " 39 | "anyway? [yn] ", player[p].name, player[p].word); 40 | 41 | line = get_line(); 42 | 43 | if(tolower(*line) == 'y') return 1; 44 | else if(tolower(*line) == 'n') return 0; 45 | } 46 | } 47 | 48 | /* TODO: decide whether the word is good or not and react accordingly */ 49 | speak(SUSIE, "\"%s\"! Excellent!\n", player[p].word); 50 | 51 | return 1; 52 | } 53 | 54 | /* callback for solve_letters to store best word */ 55 | static void store_word(const char *word, void *unused) { 56 | if(strlen(word) > strlen(best_word)) strncpy(best_word, word, 255); 57 | best_word[255] = '\0'; 58 | } 59 | 60 | /* Get dictionary corner to give their best word */ 61 | void dict_solve(const char *letters) { 62 | int i; 63 | 64 | /* TODO: this function should be more intelligent about when it gets a word 65 | that one of the players got */ 66 | 67 | best_word[0] = '\0'; 68 | 69 | solve_letters(letters, store_word, NULL); 70 | 71 | for(i = 0; best_word[i]; i++) { 72 | best_word[i] = tolower(best_word[i]); 73 | } 74 | 75 | if(!*best_word) { 76 | speak(SUSIE, "We couldn't find any words at all.\n"); 77 | } else { 78 | speak(SUSIE, "The best word we found was \"%s\".\n", best_word); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ui.js: -------------------------------------------------------------------------------- 1 | var timeout; 2 | 3 | function ui_solve_letters() { 4 | var elem = document.getElementById('letters'); 5 | var top = elem.offsetTop; 6 | while (elem = elem.offsetParent) 7 | top += elem.offsetTop; 8 | window.scrollTo(0, top); 9 | 10 | var letters = document.getElementById('letters').value; 11 | 12 | clearTimeout(timeout); 13 | if (letters.length > 5) { 14 | /* wait 5 seconds, and if no more letters come post the event */ 15 | timeout = setTimeout(function() { 16 | _gaq.push(['_trackEvent', 'solve', 'letters', letters.toLowerCase()]); 17 | }, 5000); 18 | } 19 | 20 | var result = []; 21 | 22 | solve_letters(letters.toLowerCase(), function(word, c) { result.push([word, c]); }); 23 | 24 | result.sort(function(a, b) { 25 | if (b[0].length != a[0].length) 26 | return b[0].length - a[0].length; 27 | else 28 | return b[1] - a[1]; 29 | }); 30 | 31 | document.getElementById('letters-answer').innerHTML = result.map(function(a) { return a[0]; }).join("\n"); 32 | } 33 | 34 | function ui_reset_letters() { 35 | document.getElementById('letters').value = ''; 36 | 37 | ui_solve_letters(); 38 | } 39 | 40 | function _ui_solve_numbers(trickshot) { 41 | var numbers = []; 42 | 43 | for (var i = 1; i <= 6; i++) { 44 | var x = parseInt(document.getElementById('num'+i).value, 10); 45 | 46 | if (!isNaN(x)) 47 | numbers.push(x); 48 | } 49 | 50 | var target = parseInt(document.getElementById('target').value, 10); 51 | 52 | if (numbers.length == 6) 53 | _gaq.push(['_trackEvent', 'solve', trickshot ? 'trickshot' : 'numbers', numbers.join(',')+','+target]); 54 | 55 | if (isNaN(target)) { 56 | document.getElementById('numbers-answer').innerHTML = 'Invalid target'; 57 | return; 58 | } 59 | 60 | if (numbers.length < 2) { 61 | document.getElementById('numbers-answer').innerHTML = 'Not enough numbers'; 62 | return; 63 | } 64 | 65 | document.getElementById('numbers-answer').innerHTML = solve_numbers(numbers, target, trickshot); 66 | } 67 | 68 | function ui_solve_numbers() { 69 | _ui_solve_numbers(false); 70 | } 71 | 72 | function ui_solve_trickshot() { 73 | _ui_solve_numbers(true); 74 | } 75 | 76 | function ui_reset_numbers() { 77 | for (var i = 1; i <= 6; i++) { 78 | document.getElementById('num'+i).value = ''; 79 | } 80 | document.getElementById('target').value = ''; 81 | 82 | ui_solve_numbers(); 83 | } 84 | 85 | document.write(""); 86 | -------------------------------------------------------------------------------- /old/ga/ga-numbers: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # solve the numbers game with a GA (an experiment more than a useful tool) 3 | # James Stanley 2013 4 | 5 | use strict; 6 | use warnings; 7 | 8 | my $target = 344; 9 | my @nums = (10, 2, 75, 100, 2, 2); 10 | my @OPS = ('+', '-', '*', '/'); 11 | 12 | my @population; 13 | push @population, random_individual() for (1 .. 12); 14 | 15 | my $best_individual; 16 | my $best_score; 17 | 18 | $SIG{ALRM} = sub { 19 | print stringify($best_individual); 20 | exit 0; 21 | }; 22 | 23 | # calculate for up to 5 seconds 24 | alarm 5; 25 | 26 | while (1) { 27 | my %score; 28 | 29 | $score{$_} = evaluate($_) for @population; 30 | 31 | my @winners = sort { abs($target-$score{$a}) <=> abs($target-$score{$b}) } @population; 32 | @winners = @winners[0..2]; 33 | 34 | if (!defined $best_score or abs($target-$score{$winners[0]}) < abs($target-$best_score)) { 35 | $best_individual = $winners[0]; 36 | $best_score = $score{$winners[0]}; 37 | print stringify($best_individual) if $best_score == $target; 38 | exit 0 if $best_score == $target; 39 | } 40 | 41 | @population = breed(@winners); 42 | } 43 | 44 | sub stringify { 45 | my ($genes) = @_; 46 | 47 | my @v = (@nums); 48 | 49 | for (1 .. 5) { 50 | $genes =~ s/(.)(.)(.)//; 51 | my ($a, $op, $b) = ($1-1, $2, $3-1); 52 | $b++ if $b == $a; 53 | my ($i, $j) = ($v[$a % @v], $v[$b % @v]); 54 | next if $op eq '/' && $j == 0; 55 | $v[$a % @v] = eval "$i $op $j"; 56 | print STDERR $@ if $@; 57 | my $r = $v[$a % @v]; 58 | print "$i $op $j = $r\n"; 59 | splice @v, $b%@v, 1; 60 | } 61 | } 62 | 63 | sub breed { 64 | my @parents = @_; 65 | 66 | my @population; 67 | 68 | # crossover of all parents 69 | for my $idx1 (0 .. $#parents) { 70 | for my $idx2 (0 .. $#parents) { 71 | next if $idx1 == $idx2; 72 | 73 | my $cross = int rand 15; 74 | 75 | push @population, substr($parents[$idx1], 0, $cross) . substr($parents[$idx2], $cross); 76 | } 77 | } 78 | 79 | # mutation of some individuals 80 | for my $i (0 .. $#population) { 81 | next if rand() < 0.1; 82 | 83 | my $change = int rand 15; 84 | 85 | if(substr($population[$i], $change, 1) =~ /\d/) { 86 | substr($population[$i], $change, 1, int rand 6); 87 | } else { 88 | substr($population[$i], $change, 1, $OPS[rand @OPS]); 89 | } 90 | 91 | if ($population[$i] !~ /^(\d[\+\*\/-]\d){5}$/) { 92 | die $population[$i]; 93 | } 94 | } 95 | 96 | return @population; 97 | } 98 | 99 | sub evaluate { 100 | my ($genes) = @_; 101 | 102 | my @v = (@nums); 103 | 104 | for (1 .. 5) { 105 | $genes =~ s/(.)(.)(.)//; 106 | my ($a, $op, $b) = ($1-1, $2, $3-1); 107 | $b++ if $b == $a; 108 | my ($i, $j) = ($v[$a % @v], $v[$b % @v]); 109 | next if $op eq '/' && $j == 0; 110 | $v[$a % @v] = eval "$i $op $j"; 111 | print STDERR $@ if $@; 112 | splice @v, $b%@v, 1; 113 | } 114 | 115 | return $v[0]; 116 | } 117 | 118 | sub random_individual { 119 | my $i = ''; 120 | 121 | for (1 .. 5) { 122 | $i .= 1 + int rand 6; 123 | $i .= $OPS[rand @OPS]; 124 | $i .= 1 + int rand 6; 125 | } 126 | 127 | return $i; 128 | } 129 | -------------------------------------------------------------------------------- /old/game/opts.c: -------------------------------------------------------------------------------- 1 | /* Option parsing for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | char *program_name; 8 | 9 | int players = 1; 10 | int timer = 30; 11 | char *format = "llllngtllllontlllnc"; 12 | int nocolour = 0; 13 | char *dictionary_path = DEFAULT_DICT; 14 | int ignore_invalid = 0; 15 | int noflush = 0; 16 | int num_letters = 9; 17 | 18 | static struct option opts[] = { 19 | { "colour", no_argument, NULL, 'c' }, 20 | { "dictionary", required_argument, NULL, 'd' }, 21 | { "format", required_argument, NULL, 'f' }, 22 | { "help", no_argument, NULL, 'h' }, 23 | { "ignore-invalid", no_argument, NULL, 'i' }, 24 | { "letters", required_argument, NULL, 'l' }, 25 | { "no-colour", no_argument, NULL, 'n' }, 26 | { "players", required_argument, NULL, 'p' }, 27 | { "no-flush", no_argument, NULL, 's' }, 28 | { "timer", required_argument, NULL, 't' }, 29 | { NULL, 0, NULL, 0 } 30 | }; 31 | 32 | static void usage(void) { 33 | printf( 34 | "cntdn - countdown game simulator\n" 35 | "Usage: %s [OPTIONS...]\n" 36 | "\n" 37 | "Options:\n" 38 | " -c, --colour Enable coloured output (default if stdout is a\n" 39 | " terminal)\n" 40 | " -d, --dictionary=STR Set the path to the dictionary file (default:\n" 41 | " " DEFAULT_DICT ")\n" 42 | " -f, --format=STR Set the game format as described below (default:\n" 43 | " llllngtllllontlllnc)\n" 44 | " -h, --help Display this help\n" 45 | " -i, --ignore-invalid Suppress warning about invalid words in the\n" 46 | " dictionary\n" 47 | " -l, --letters=N Set the number of letters in the letters game\n" 48 | " -n, --no-colour Disable coloured output\n" 49 | " -p, --players=N Set number of players (default: 1)\n" 50 | " -s, --no-flush Don't discard stdin text before reading it\n" 51 | " -t, --timer=N Set round timer in seconds (default: 30)\n" 52 | "\n" 53 | "The game format is described as a series of characters corresponding to\n" 54 | "game rounds. The letter meanings are:\n" 55 | " l - Letters round n - Numbers round g - Guest chat\n" 56 | " t - Teatime teaser o - Origin of words c - Countdown conundrum\n" 57 | "\n" 58 | "Report bugs to James Stanley \n" 59 | , program_name); 60 | } 61 | 62 | /* parse command line options. this function exit()'s if appropriate */ 63 | void parse_opts(int argc, char **argv) { 64 | int c; 65 | 66 | program_name = argv[0]; 67 | 68 | if(!isatty(STDOUT_FILENO)) nocolour = 1; 69 | 70 | opterr = 1; 71 | 72 | while((c = getopt_long(argc, argv, "cd:f:hil:np:st:", opts, NULL)) != -1) { 73 | switch(c) { 74 | case 'c': nocolour = 0; break; 75 | case 'd': dictionary_path = optarg; break; 76 | case 'f': format = optarg; break; 77 | case 'h': usage(); exit(0); break; 78 | case 'i': ignore_invalid = 1; break; 79 | case 'l': num_letters = atoi(optarg); break; 80 | case 'n': nocolour = 1; break; 81 | case 'p': players = atoi(optarg); break; 82 | case 's': noflush = 1; break; 83 | case 't': timer = atoi(optarg); break; 84 | default: exit(1); break; 85 | } 86 | } 87 | 88 | /* set escape codes to be empty */ 89 | if(nocolour) colours_off(); 90 | 91 | /* validate options */ 92 | if(timer < 0) 93 | die("error: timer must be at least 0 seconds"); 94 | if(num_letters < 1) 95 | die("error: must have at least 1 letter in the letters game"); 96 | if(num_letters > 255) 97 | die("error: can't have more than 255 letters"); 98 | } 99 | -------------------------------------------------------------------------------- /old/numbers.c: -------------------------------------------------------------------------------- 1 | /* numbers - Countdown numbers game solver 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "numbers.h" 6 | 7 | #define ADD 0 8 | #define SUB1 1 9 | #define SUB2 2 10 | #define MUL 3 11 | #define DIV1 4 12 | #define DIV2 5 13 | 14 | static int sp = 0; 15 | static int stack[32]; 16 | 17 | int numbers; 18 | 19 | /* apply operation o to n1 and n2 and return the result 20 | fails silently on division by zero or invalid operator */ 21 | int op(int o, int n1, int n2) { 22 | switch(o) { 23 | case ADD : return n1 + n2; 24 | case SUB1: return n1 - n2; 25 | case SUB2: return n2 - n1; 26 | case MUL : return n1 * n2; 27 | case DIV1: return n1 / n2; 28 | case DIV2: return n2 / n1; 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | /* push the given values on to the output stack 35 | to output in form "n1 o n2 = r" */ 36 | void push_vals(int n1, int o, int n2, int r) { 37 | if(o == SUB2 || o == DIV2) { 38 | stack[sp++] = n2; 39 | stack[sp++] = o; 40 | stack[sp++] = n1; 41 | } else { 42 | stack[sp++] = n1; 43 | stack[sp++] = o; 44 | stack[sp++] = n2; 45 | } 46 | stack[sp++] = r; 47 | } 48 | 49 | /* prints the operands/operators stored on the stack */ 50 | void print_vals(void) { 51 | static char opname[6] = { '+', '-', '-', '*', '/', '/' }; 52 | int n1, o, n2, r; 53 | 54 | while(sp) { 55 | r = stack[--sp]; 56 | n2 = stack[--sp]; 57 | o = stack[--sp]; 58 | n1 = stack[--sp]; 59 | 60 | printf("%d %c %d = %d\n", n1, opname[o], n2, r); 61 | } 62 | 63 | printf("Solved.\n"); 64 | } 65 | 66 | /* recursively solve the game, up to "levels" levels of recursion */ 67 | static int recurse_solve(int levels) { 68 | int i, j, o; 69 | int k; 70 | int ni, nj; 71 | int result; 72 | 73 | if(levels == 0) return 0; 74 | 75 | for(i = 0; i < numbers - 1; i++) { 76 | ni = number[i]; 77 | 78 | for(j = i + 1; j < numbers; j++) { 79 | nj = number[j]; 80 | 81 | /* one number used up */ 82 | numbers--; 83 | 84 | /* move everything after position j along to fill the gap */ 85 | for(k = j; k < numbers; k++) number[k] = number[k + 1]; 86 | 87 | for(o = 0; o < 6; o++) { 88 | if((o == DIV1) && (nj == 0 || ni % nj != 0)) continue; 89 | if((o == DIV2) && (ni == 0 || nj % ni != 0)) continue; 90 | if((o == SUB1) && (nj > ni)) continue; 91 | if((o == SUB2) && (ni > nj)) continue; 92 | 93 | /* store (ni ? nj) at position i 94 | we have to store in result as well so that when we output the 95 | answer the shortcut stack unwinding could have the wrong value 96 | in number[i] */ 97 | number[i] = result = op(o, ni, nj); 98 | 99 | /* if the result is the target, we short-circuit and push values, 100 | otherwise solve() is called, and if it returns 1 we push values */ 101 | if(result == target || recurse_solve(levels - 1)) { 102 | push_vals(ni, o, nj, result); 103 | return 1; 104 | } 105 | } 106 | 107 | /* move numbers along to make space at j */ 108 | for(k = numbers; k > j; k--) number[k] = number[k - 1]; 109 | 110 | /* put j back in */ 111 | number[j] = nj; 112 | 113 | /* extra number present again */ 114 | numbers++; 115 | } 116 | 117 | /* put i back in */ 118 | number[i] = ni; 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | /* solve the game, returning 1 if solved and 0 otherwise */ 125 | int solve(void) { 126 | int i; 127 | 128 | numbers = 6; 129 | 130 | /* see if one of these numbers is the answer */ 131 | for(i = 0; i < 6; i++) { 132 | if(number[i] == target) return 1; 133 | } 134 | 135 | /* iteratively deepen the DFS */ 136 | for(i = 2; i <= 6; i++) { 137 | if(recurse_solve(i)) return 1; 138 | } 139 | 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /old/letters_main.c: -------------------------------------------------------------------------------- 1 | /* Main source file for the letters game solver 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "letters.h" 6 | 7 | int ignore_invalid = 0; 8 | int interactive_mode = 0; 9 | int minletters = 6; 10 | 11 | static struct option opts[] = { 12 | { "dictionary", required_argument, NULL, 'd' }, 13 | { "help", no_argument, NULL, 'h' }, 14 | { "ignore-invalid", no_argument, NULL, 'i' }, 15 | { "interactive", no_argument, NULL, 'I' }, 16 | { "min-letters", required_argument, NULL, 'm' }, 17 | { NULL, 0, NULL, 0 } 18 | }; 19 | 20 | /* print the reason on stderr and terminate the program */ 21 | void die(const char *fmt, ...) { 22 | va_list args; 23 | 24 | va_start(args, fmt); 25 | vfprintf(stderr, fmt, args); 26 | va_end(args); 27 | 28 | fputc('\n', stderr); 29 | 30 | exit(1); 31 | } 32 | 33 | static void usage(void) { 34 | printf( 35 | "letters - countdown letters game solver\n" 36 | "Usage: letters [OPTIONS...] LETTERS\n" 37 | "\n" 38 | "Options:\n" 39 | " -d, --dictionary=STR Set the path to the dictionary file (default:\n" 40 | " " DEFAULT_DICT ")\n" 41 | " -h, --help Display this help\n" 42 | " -i, --ignore-invalid Suppress warnings about invalid words in the\n" 43 | " dictionary\n" 44 | " -I, --interactive Use interactive mode; see below for details\n" 45 | " -m, --min-letters=N Set minimum number of letters (default: 6)\n" 46 | "\n" 47 | "For example, if the dictionary is in /usr/share/dict/words and the letters\n" 48 | "you have are GYHDNOEUR, you could invoke letters like so:\n" 49 | " letters --dictionary=/usr/share/dict/words GYHDNOEUR\n" 50 | "\n" 51 | "Interactive mode should be used when performance is important and you have\n" 52 | "more than one set of letters that need solving. Give a stream of letter\n" 53 | "sets, one per line, on stdin. This program will give all of the solutions\n" 54 | "for the first set, followed by a blank line, followed by the next set,\n" 55 | "etc.\n" 56 | "\n" 57 | "Report bugs to James Stanley \n" 58 | ); 59 | } 60 | 61 | /* callback for solve_letters() to output words of at least minletters */ 62 | static void output_words(const char *word, void *unused) { 63 | if(strlen(word) >= minletters) puts(word); 64 | } 65 | 66 | int main(int argc, char **argv) { 67 | int c; 68 | char *dict = DEFAULT_DICT; 69 | char *letters; 70 | char buf[512]; 71 | char *p; 72 | 73 | opterr = 1; 74 | 75 | while((c = getopt_long(argc, argv, "d:hiIm:", opts, NULL)) != -1) { 76 | switch(c) { 77 | case 'd': dict = optarg; break; 78 | case 'h': usage(); exit(0); break; 79 | case 'i': ignore_invalid = 1; break; 80 | case 'I': interactive_mode = 1; break; 81 | case 'm': minletters = atoi(optarg); break; 82 | default: exit(1); break; 83 | } 84 | } 85 | 86 | if(interactive_mode) { 87 | if(optind < argc) 88 | die("error: you must specify your letters on stdin when using " 89 | "interactive mode; see --help for more information"); 90 | 91 | load_dictionary(dict, 0); 92 | 93 | /* disable block buffering */ 94 | setvbuf(stdout, NULL, _IOLBF, 0); 95 | 96 | /* read letters from stdin until EOF */ 97 | while(fgets(buf, 512, stdin)) { 98 | if((p = strchr(buf, '\n'))) *p = '\0'; 99 | 100 | solve_letters(buf, output_words, NULL); 101 | 102 | putchar('\n'); 103 | fflush(stdout); 104 | } 105 | } else /* not interactive_mode */ { 106 | if(optind >= argc) 107 | die("error: you must specify your letters; see --help for more " 108 | "information"); 109 | 110 | letters = argv[optind]; 111 | 112 | load_dictionary(dict, strlen(letters)); 113 | 114 | solve_letters(letters, output_words, NULL); 115 | } 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /old/game/strings.c: -------------------------------------------------------------------------------- 1 | /* String lists for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | /* random player names */ 8 | static const char *name_string[] = { 9 | "Augustus", "Billy", "Dwayne", "Edmond", "Eric", "Jed", "Joanne", "Susanne", 10 | "Sylvester", "Wayne", "Zeb" 11 | }; 12 | static const int num_names = sizeof(name_string) / sizeof(*name_string); 13 | 14 | /* letter frequencies from http://www.thecountdownpage.com/letters.htm */ 15 | static const char vowel_string[] = 16 | "AAAAAAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEIIIIIIIIIIIIIOOOOOOOOOOOOOUUUUU"; 17 | static const int num_vowels = 18 | sizeof(vowel_string) / sizeof(*vowel_string) - 1; 19 | 20 | static const char cons_string[] = 21 | "BBCCCDDDDDDFFGGGHHJKLLLLLMMMMNNNNNNNNPPPPQRRRRRRRRRSSSSSSSSSTTTTTTTTTVWXYZ"; 22 | static const int num_consonants = 23 | sizeof(cons_string) / sizeof(*cons_string) - 1; 24 | 25 | /* a shuffled array of vowels. this is re-initialised from vowel_string whenever 26 | it becomes empty, and then letters are subsequently read from vowel[0], 27 | vowel[1], vowel[2], etc. */ 28 | static char vowel[sizeof(vowel_string) / sizeof(*vowel_string)]; 29 | static int vowel_pos = sizeof(vowel) / sizeof(*vowel); 30 | static char consonant[sizeof(cons_string) / sizeof(*cons_string)]; 31 | static int cons_pos = sizeof(consonant) / sizeof(*consonant); 32 | 33 | /* number choices for the numbers game */ 34 | static const int large_nums[] = { 25, 50, 75, 100 }; 35 | static const int num_large = sizeof(large_nums) / sizeof(*large_nums); 36 | 37 | static const int small_nums[] = { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 38 | 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 }; 39 | static const int num_small = sizeof(small_nums) / sizeof(*small_nums); 40 | 41 | /* a shuffled array of large numbers, re-initialised whenever it becomes empty 42 | or by init_numbers() when a numbers game starts */ 43 | static int large[sizeof(large_nums) / sizeof(*large_nums)]; 44 | static int large_pos = sizeof(large) / sizeof(*large); 45 | static int small[sizeof(small_nums) / sizeof(*small_nums)]; 46 | static int small_pos = sizeof(small) / sizeof(*small); 47 | 48 | /* ANSI colours */ 49 | const char *colour_off = "\e[0m"; 50 | const char *letter_colour = "\e[1;37;44m"; 51 | const char *number_colour = "\e[1;41m"; 52 | 53 | /* player colours */ 54 | const char *colour[] = { 55 | "\e[31m", "\e[32m", "\e[33m", "\e[34m", "\e[35m", "\e[36m" 56 | }; 57 | const int num_colours = sizeof(colour) / sizeof(*colour); 58 | 59 | /* presenter names */ 60 | static const char *pres_name[] = { 61 | "Jeff", "Susie", "Rachel", "MYSTERY GUEST" 62 | }; 63 | 64 | /* presenter colours */ 65 | static const char *pres_colour[] = { 66 | /* Jeff */ "\e[1;31m", /* Susie */ "\e[1;32m", 67 | /* Rachel */ "\e[1;35m", /* Guest */ "\e[1;34m" 68 | }; 69 | 70 | /* shuffles character array 'a' of length 'len' */ 71 | static void char_shuffle(char *a, int len) { 72 | int i, j; 73 | char c; 74 | 75 | for(i = len-1; i >= 0; i--) { 76 | j = rand() % (i + 1); 77 | c = a[i]; 78 | a[i] = a[j]; 79 | a[j] = c; 80 | } 81 | } 82 | 83 | /* shuffles integer array 'a' of length 'len' */ 84 | static void int_shuffle(int *a, int len) { 85 | int i, j; 86 | int c; 87 | 88 | for(i = len-1; i >= 0; i--) { 89 | j = rand() % (i + 1); 90 | c = a[i]; 91 | a[i] = a[j]; 92 | a[j] = c; 93 | } 94 | } 95 | 96 | /* initialise the shuffled vowel array */ 97 | static void init_vowels(void) { 98 | memcpy(vowel, vowel_string, num_vowels); 99 | char_shuffle(vowel, num_vowels); 100 | vowel_pos = 0; 101 | } 102 | 103 | /* initialise the shuffled consonant array */ 104 | static void init_consonants(void) { 105 | memcpy(consonant, cons_string, num_consonants); 106 | char_shuffle(consonant, num_consonants); 107 | cons_pos = 0; 108 | } 109 | 110 | /* initialise the shuffled number arrays */ 111 | void init_numbers(void) { 112 | memcpy(large, large_nums, sizeof(large)); 113 | memcpy(small, small_nums, sizeof(small)); 114 | int_shuffle(large, num_large); 115 | int_shuffle(small, num_small); 116 | large_pos = 0; 117 | small_pos = 0; 118 | } 119 | 120 | /* return a random player name */ 121 | const char *get_name(void) { 122 | return name_string[rand() % num_names]; 123 | } 124 | 125 | /* return a weighted random vowel */ 126 | char get_vowel(void) { 127 | if(vowel_pos >= num_vowels) init_vowels(); 128 | return vowel[vowel_pos++]; 129 | } 130 | 131 | /* return a weighted random consonant */ 132 | char get_consonant(void) { 133 | if(cons_pos >= num_consonants) init_consonants(); 134 | return consonant[cons_pos++]; 135 | } 136 | 137 | /* return a small number */ 138 | int get_small(void) { 139 | if(small_pos >= num_small) init_numbers(); 140 | return small[small_pos++]; 141 | } 142 | 143 | /* return a large number */ 144 | int get_large(void) { 145 | if(large_pos >= num_large) init_numbers(); 146 | return large[large_pos++]; 147 | } 148 | 149 | /* replace colour strings with "" */ 150 | void colours_off(void) { 151 | int i; 152 | 153 | for(i = 0; i < num_colours; i++) colour[i] = ""; 154 | for(i = 0; i <= GUEST; i++) pres_colour[i] = ""; 155 | letter_colour = ""; 156 | colour_off = ""; 157 | } 158 | 159 | /* Speaks a line of text from the given presenter */ 160 | void speak(int presenter, const char *fmt, ...) { 161 | va_list args, args2; 162 | int len; 163 | char *s; 164 | 165 | printf("%s%s%s: ", pres_colour[presenter], pres_name[presenter], colour_off); 166 | 167 | va_start(args, fmt); 168 | 169 | /* find the length required to store the text */ 170 | va_copy(args2, args); 171 | len = vsnprintf(NULL, 0, fmt, args2); 172 | va_end(args2); 173 | 174 | /* allocate the string and write the text */ 175 | s = malloc(len + 1); 176 | vsnprintf(s, len + 1, fmt, args); 177 | 178 | va_end(args); 179 | 180 | slow_printf("%s", s); 181 | 182 | free(s); 183 | } 184 | 185 | /* printf, but slowly */ 186 | void slow_printf(const char *fmt, ...) { 187 | va_list args, args2; 188 | char *s, *p; 189 | int len; 190 | 191 | va_start(args, fmt); 192 | 193 | /* find the length required to store the text */ 194 | va_copy(args2, args); 195 | len = vsnprintf(NULL, 0, fmt, args2); 196 | va_end(args2); 197 | 198 | /* allocate the string and write the text */ 199 | s = malloc(len + 1); 200 | vsnprintf(s, len + 1, fmt, args); 201 | 202 | va_end(args); 203 | 204 | /* slowly output the words */ 205 | for(p = s; *p; p++) { 206 | putchar(*p); 207 | fflush(stdout); 208 | if(*p == ' ') usleep(150000); 209 | if(*p == '\n') usleep(500000); 210 | } 211 | 212 | free(s); 213 | 214 | return; 215 | } 216 | -------------------------------------------------------------------------------- /old/letters.c: -------------------------------------------------------------------------------- 1 | /* Letters game solver for cntdn 2 | 3 | This file is used by cntdn and lettersd as well as by the stand-alone solver 4 | 5 | James Stanley 2011 */ 6 | 7 | #include "letters.h" 8 | 9 | TrieNode *dictionary; 10 | 11 | /* this table is filled in by load_dictionary(). Before this table was 12 | introduced, 40% of execution time in solving letters (excluding loading the 13 | dictionary) was spent calculating letter indices. The table reduces that to 14 | about 1% */ 15 | static int idx_table[256]; 16 | #define letter_idx(c) (idx_table[(unsigned int)(c & 0xff)]) 17 | 18 | /* allocate an empty node for the trie, setting all of the fields to zero */ 19 | static TrieNode *make_trienode(void) { 20 | TrieNode *n; 21 | 22 | n = malloc(sizeof(TrieNode)); 23 | memset(n, 0, sizeof(TrieNode)); 24 | 25 | return n; 26 | } 27 | 28 | /* add the given word to the dictionary trie */ 29 | static void add_word(const char *word) { 30 | static int count_invalid = 0; 31 | TrieNode *n; 32 | const char *p; 33 | int idx; 34 | 35 | n = dictionary; 36 | p = word; 37 | 38 | while(*p) { 39 | if((idx = letter_idx(*p)) == -1) {/* invalid letter */ 40 | if(ignore_invalid) return; 41 | 42 | if(count_invalid < 5) { 43 | fprintf(stderr, "warning: '%s' contains a non-letter '%c', ignoring " 44 | "word; use --ignore-invalid to suppress these " 45 | "warnings\n", 46 | word, *p); 47 | count_invalid++; 48 | } else if(count_invalid == 5) { 49 | fprintf(stderr, "warning: more invalid words; suppressing warnings\n"); 50 | count_invalid = 6; 51 | } 52 | 53 | return; 54 | } 55 | 56 | if(!n->child[idx]) n->child[idx] = make_trienode(); 57 | 58 | /* go to next node and next character */ 59 | n = n->child[idx]; 60 | p++; 61 | } 62 | 63 | /* end of the word, mark as such */ 64 | n->end_word = 1; 65 | } 66 | 67 | /* Load the dictionary trie from 'path'. set 'maxlen' to the maximum length for 68 | words to be allowed in the dictionary, or 0 for no limit */ 69 | void load_dictionary(const char *path, int maxlen) { 70 | FILE *fp; 71 | char *p; 72 | char buf[BUFLEN]; 73 | int i; 74 | 75 | /* initialise the letter_idx table */ 76 | for(i = 0; i < 256; i++) { 77 | if(i >= 'A' && i <= 'Z') idx_table[i] = i - 'A'; 78 | else if(i >= 'a' && i <= 'z') idx_table[i] = i - 'a'; 79 | else idx_table[i] = -1; 80 | } 81 | 82 | if(!(fp = fopen(path, "r"))) 83 | die("error: unable to open '%s' for reading; try using --dictionary", path); 84 | 85 | /* allocate the root node */ 86 | dictionary = make_trienode(); 87 | 88 | while(fgets(buf, BUFLEN, fp)) { 89 | /* accept dos files even on unix; we replace the first occurence of either 90 | \r or \n with a nul byte */ 91 | if((p = strpbrk(buf, "\r\n"))) *p = '\0'; 92 | 93 | /* add the word to the trie */ 94 | if(maxlen == 0 || strlen(buf) <= maxlen) add_word(buf); 95 | } 96 | 97 | fclose(fp); 98 | } 99 | 100 | /* Recursively solve the letters game. Do not call this function, it is used by 101 | solve_letters() to do the actual solving */ 102 | static void recurse_solve(const char *letters, TrieNode *node, char *answer, 103 | int level, int nletters, char *used_letter, 104 | void (*callback)(const char *word, void *data), 105 | void *data) { 106 | int i; 107 | int idx; 108 | char done[26] = { 0 }; 109 | 110 | /* if this node can represent the end of a word, output it */ 111 | if(node->end_word) callback(answer, data); 112 | 113 | /* if all child nodes represent words which are too long, stop searching */ 114 | if(level == nletters) return; 115 | 116 | /* for each of the letters we have */ 117 | for(i = 0; i < nletters; i++) { 118 | /* if this letter is already used do not use it again */ 119 | if(used_letter[i]) continue; 120 | 121 | /* if this is not an actual letter do not try to use it */ 122 | if((idx = letter_idx(letters[i])) == -1) continue; 123 | 124 | /* if we have already recursed on this letter index, don't bother trying 125 | * again - this prevents recursing on 'a' twice and getting duplicated 126 | * output. 127 | */ 128 | if(done[idx]) continue; 129 | 130 | /* if there is a child with this letter, recurse. 131 | * if the current node represents 'fo' and the current letter is 'o', the 132 | * child node represents 'foo'. 133 | */ 134 | if(node->child[idx]) { 135 | /* don't allow this letter to be used twice */ 136 | used_letter[i] = 1; 137 | 138 | /* append to the answer */ 139 | answer[level] = letters[i]; 140 | 141 | /* don't recurse on the same character again */ 142 | done[idx] = 1; 143 | 144 | recurse_solve(letters, node->child[idx], answer, level+1, nletters, 145 | used_letter, callback, data); 146 | 147 | used_letter[i] = 0; 148 | } 149 | } 150 | 151 | answer[level] = '\0'; 152 | } 153 | 154 | /* Solves the letters game for the given letters by calling 'callback' with 155 | each of the words found */ 156 | void solve_letters(const char *letters, 157 | void (*callback)(const char *word, void *data), 158 | void *data) { 159 | int nletters = strlen(letters); 160 | char *used_letter; 161 | char *answer; 162 | 163 | used_letter = malloc(nletters); 164 | memset(used_letter, 0, nletters); 165 | 166 | answer = malloc(nletters + 1); 167 | memset(answer, 0, nletters + 1); 168 | 169 | recurse_solve(letters, dictionary, answer, 0, nletters, used_letter, 170 | callback, data); 171 | 172 | free(answer); 173 | free(used_letter); 174 | } 175 | 176 | /* Return 1 if the given word is in the dictionary and 0 otherwise */ 177 | int word_in_dictionary(const char *word) { 178 | TrieNode *n = dictionary; 179 | const char *p = word; 180 | int idx; 181 | 182 | while(*p) { 183 | /* TODO: Should this give a warning? */ 184 | if((idx = letter_idx(*p)) == -1) return 0; 185 | 186 | if(!n->child[idx]) return 0; 187 | 188 | p++; 189 | n = n->child[idx]; 190 | 191 | /* if that was the last letter and the word can end, the word was in the 192 | dictionary */ 193 | if(!*p && n->end_word) return 1; 194 | } 195 | 196 | return 0; 197 | } 198 | 199 | /* Return 1 if the given word can be made with the given letters */ 200 | int can_make_word(const char *word, const char *letters) { 201 | int avail[26] = { 0 }; 202 | int i, idx; 203 | const char *p; 204 | 205 | /* add one availability for each letter */ 206 | for(p = letters; *p; p++) { 207 | if((idx = letter_idx(*p)) == -1) /* TODO: does this need to be fatal? */ 208 | die("error: letters array '%s' contains non-letter '%c'.", letters, *p); 209 | 210 | avail[idx]++; 211 | } 212 | 213 | /* remove one availability for each letter of the word */ 214 | for(p = word; *p; p++) { 215 | if((idx = letter_idx(*p)) == -1) return 0; 216 | 217 | avail[idx]--; 218 | } 219 | 220 | /* if any availabilities are negative, it means more of a letter was used than 221 | was available */ 222 | for(i = 0; i < 26; i++) { 223 | if(avail[i] < 0) return 0; 224 | } 225 | 226 | return 1; 227 | } 228 | -------------------------------------------------------------------------------- /old/game/games.c: -------------------------------------------------------------------------------- 1 | /* Game handling for cntdn 2 | 3 | James Stanley 2010 */ 4 | 5 | #include "cntdn.h" 6 | 7 | static char letter[256]; 8 | static int round = 1; 9 | 10 | int number[6]; 11 | int target; 12 | 13 | static sig_atomic_t stop_timer; 14 | 15 | /* signal handler to cancel the countdown timer */ 16 | static void cancel_timer(int sig) { 17 | stop_timer = 1; 18 | } 19 | 20 | static void run_timer(void) { 21 | int i; 22 | 23 | /* install signal handler that will cancel the timer */ 24 | stop_timer = 0; 25 | signal(SIGINT, cancel_timer); 26 | 27 | /* wait for players to think */ 28 | for(i = timer; i >= 0 && !stop_timer; i--) { 29 | printf("\r ");/* clear the line */ 30 | printf("\r%d second%s left.", i, (i == 1 ? "" : "s")); 31 | fflush(stdout); 32 | if(i > 0) sleep(1); 33 | } 34 | printf("\n"); 35 | 36 | /* re-instate default SIGINT handler */ 37 | signal(SIGINT, SIG_DFL); 38 | } 39 | 40 | /* get the nth letter for the current round */ 41 | static void get_letter(int n) { 42 | char *line; 43 | int i; 44 | 45 | while(1) { 46 | printf("vowel or consonant? [vc] "); 47 | line = get_line(); 48 | 49 | if(tolower(*line) == 'v') { 50 | letter[n] = get_vowel(); 51 | break; 52 | } else if(tolower(*line) == 'c') { 53 | letter[n] = get_consonant(); 54 | break; 55 | } 56 | 57 | /* TODO: limitations on amounts of vowels and consonants */ 58 | } 59 | 60 | if(nocolour) printf("| "); 61 | else printf("%s ", letter_colour); 62 | 63 | for(i = 0; i < num_letters; i++) { 64 | printf("%c ", (i <= n ? letter[i] : ' ')); 65 | } 66 | 67 | if(nocolour) printf("|\n"); 68 | else printf("%s\n", colour_off); 69 | } 70 | 71 | /* sort by word length for qsort */ 72 | static int length_cmp(const void *one, const void *two) { 73 | const int *a = one, *b = two; 74 | 75 | return player[*a].length - player[*b].length; 76 | } 77 | 78 | /* play one letters round */ 79 | void letters_round(void) { 80 | static int turn = 0; 81 | int i, j, k; 82 | int *player_order; 83 | 84 | /* stages: 85 | pick letters 86 | 30 second timer (let dictionary corner think during this time?) 87 | reveal word lengths 88 | reveal words 89 | assign scores 90 | dictionary corner words */ 91 | printf(" Round %d: Letters round\n", round); 92 | 93 | printf("It is %s's turn to choose letters.\n", player[turn].name); 94 | 95 | /* read letter choices and generate letters */ 96 | for(i = 0; i < num_letters; i++) get_letter(i); 97 | 98 | /* let people think */ 99 | run_timer(); 100 | 101 | /* ask each player for their word length */ 102 | for(i = 0; i < players; i++) { 103 | printf("%s, how long is your word? ", player[(i + turn) % players].name); 104 | player[(i + turn) % players].length = atoi(get_line()); 105 | } 106 | 107 | /* get an aray of players sorted by word length */ 108 | player_order = malloc(sizeof(int) * players); 109 | for(i = 0; i < players; i++) player_order[i] = i; 110 | qsort(player_order, players, sizeof(int), length_cmp); 111 | 112 | /* ask players for their word, shortest first */ 113 | for(j = 0; j < players; j++) { 114 | i = player_order[j]; 115 | 116 | if(player[i].length <= 0) { 117 | player[i].word = NULL; 118 | continue; 119 | } 120 | 121 | printf("%s, what is your %d-letter word? ", player[i].name, 122 | player[i].length); 123 | player[i].word = strdup(get_line()); 124 | 125 | if(strlen(player[i].word) != player[i].length 126 | || !valid_word(i, letter)) { 127 | free(player[i].word); 128 | 129 | /* try again once if they didn't supply a suitable word */ 130 | printf("%s, what is your real %d-letter word? ", player[i].name, 131 | player[i].length); 132 | player[i].word = strdup(get_line()); 133 | 134 | if(strlen(player[i].word) != player[i].length 135 | || !valid_word(i, letter)) player[i].length = 0; 136 | } 137 | } 138 | 139 | /* re-sort to get non-words removed */ 140 | qsort(player_order, players, sizeof(int), length_cmp); 141 | 142 | /* find the best scorers */ 143 | for(j = players - 2; j >= 0; j--) { 144 | i = player_order[j]; 145 | 146 | if(player[i].length < player[player_order[players - 1]].length) break; 147 | } 148 | 149 | /* assign points to the scorers */ 150 | for(k = players - 1; k > j; k--) { 151 | i = player_order[k]; 152 | 153 | if(player[i].length == num_letters) {/* 9 letters score double */ 154 | printf("%d points to %s.\n", num_letters * 2, player[i].name); 155 | player[i].score += num_letters * 2; 156 | } else { 157 | printf("%d points to %s.\n", player[i].length, player[i].name); 158 | player[i].score += player[i].length; 159 | } 160 | } 161 | 162 | /* TODO: display best word in the blue and white style */ 163 | 164 | /* ask dictionary corner if they got anything better */ 165 | dict_solve(letter); 166 | 167 | /* TODO: display dictionary corner's word in blue and white */ 168 | 169 | /* increment the player whose turn it is to choose letters */ 170 | round++; 171 | turn++; 172 | if(turn >= players) turn = 0; 173 | 174 | /* tidy up */ 175 | free(player_order); 176 | for(i = 0; i < players; i++) free(player[i].word); 177 | } 178 | 179 | /* play one numbers round */ 180 | void numbers_round(void) { 181 | int num_large; 182 | int i, j; 183 | static int turn = 0; 184 | 185 | /* stages: 186 | pick numbers 187 | 30 second timer (let Rachel think during this time?) 188 | reveal answers 189 | reveal methods 190 | assign scores 191 | Rachel's solution */ 192 | printf(" Round %d: Numbers round\n", round); 193 | 194 | printf("It is %s's turn to choose numbers.\n", player[turn].name); 195 | 196 | /* choose numbers */ 197 | init_numbers(); 198 | do { 199 | printf("%s, how many large numbers? [0 to 4] ", player[turn].name); 200 | num_large = atoi(get_line()); 201 | } while(num_large < 0 || num_large > 4); 202 | 203 | /* generate numbers */ 204 | for(i = 0; i < num_large; i++) { 205 | number[i] = get_large(); 206 | } 207 | for(; i < 6; i++) { 208 | number[i] = get_small(); 209 | } 210 | 211 | /* display numbers */ 212 | for(j = 6; j >= 0; j--) { 213 | printf("\r"); 214 | 215 | if(nocolour) printf("| "); 216 | else printf("%s ", letter_colour); 217 | 218 | for(i = 0; i < j; i++) { 219 | if(number[i] > 9) printf(" "); 220 | if(number[i] > 99) printf(" "); 221 | printf(" "); 222 | } 223 | for(i = j; i < 6; i++) { 224 | printf("%d ", number[i]); 225 | } 226 | 227 | if(nocolour) printf("|"); 228 | else printf("%s", colour_off); 229 | fflush(stdout); 230 | 231 | usleep(500000); 232 | } 233 | putchar('\n'); 234 | 235 | speak(RACHEL, "And the target number is...\n"); 236 | 237 | /* generate a target */ 238 | for(i = 0; i < 20; i++) { 239 | printf(" \r"); 240 | if(!nocolour) printf("%s", number_colour); 241 | 242 | printf(" %d ", 100 + (rand() % 900)); 243 | 244 | if(!nocolour) printf("%s", colour_off); 245 | fflush(stdout); 246 | 247 | usleep(100000); 248 | } 249 | target = 100 + (rand() % 900); 250 | printf(" \r"); 251 | if(!nocolour) printf("%s", number_colour); 252 | 253 | printf(" %d ", target); 254 | 255 | if(!nocolour) printf("%s", colour_off); 256 | printf("\n"); 257 | 258 | /* let people think */ 259 | run_timer(); 260 | 261 | /* increment the player whose turn it is to choose numbers */ 262 | round++; 263 | turn++; 264 | if(turn >= players) turn = 0; 265 | } 266 | 267 | /* do a teatime teaser */ 268 | void teatime_teaser(void) { 269 | printf(" *** Teatime teaser\n"); 270 | } 271 | 272 | /* play the conundrum */ 273 | void conundrum(void) { 274 | printf(" *** Conundrum\n"); 275 | } 276 | -------------------------------------------------------------------------------- /old/lettersd.c: -------------------------------------------------------------------------------- 1 | /* Daemon to solve letter sets for cntdn 2 | 3 | Networking information from: 4 | http://beej.us/guide/bgnet/output/html/multipage/clientserver.html 5 | 6 | James Stanley 2011 */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "letters.h" 17 | 18 | int ignore_invalid = 1; 19 | int minletters = 0; 20 | 21 | volatile sig_atomic_t nclients = 0; 22 | 23 | static struct option opts[] = { 24 | { "dictionary", required_argument, NULL, 'd' }, 25 | { "help", no_argument, NULL, 'h' }, 26 | { "listen-addr", required_argument, NULL, 'l' }, 27 | { "max-clients", required_argument, NULL, 'c' }, 28 | { "port", required_argument, NULL, 'p' }, 29 | { NULL, 0, NULL, 0 } 30 | }; 31 | 32 | void die(const char *fmt, ...) { 33 | fprintf(stderr, "Not dying. Segfault expected.\n"); 34 | } 35 | 36 | void sigpipe(int sig) { 37 | fprintf(stderr, "warning: SIGPIPE received!\n"); 38 | } 39 | 40 | static void usage(void) { 41 | printf( 42 | "lettersd - daemon for the countdown letters game solver\n" 43 | "Usage: lettersd [OPTIONS...]\n" 44 | "\n" 45 | "Options:\n" 46 | " -D, --daemonize Daemonize instead of running in the foreground\n" 47 | " -d, --dictionary Set the path to the dictionary file (default:\n" 48 | " " DEFAULT_DICT ")\n" 49 | " -h, --help Display this help\n" 50 | " -l, --listen-addr Set the address to listen on (default: localhost)\n" 51 | " -c, --max-clients Set the maximum number of concurrent clients\n" 52 | " (default: -1)\n" 53 | " -p, --port Set the port number to listen on (default: 17220)\n" 54 | "\n" 55 | "For the --max-clients option a negative value means no limit.\n" 56 | "\n" 57 | "Report bugs to James Stanley \n" 58 | ); 59 | } 60 | 61 | /* open a socket on the given address and port, and return the associated fd */ 62 | static int start_listening(const char *addr, const char *port) { 63 | struct addrinfo hints, *servinfo, *p; 64 | int num_addrs; 65 | int yes = 1; 66 | int fd; 67 | int n; 68 | 69 | memset(&hints, 0, sizeof(hints)); 70 | hints.ai_family = AF_UNSPEC; 71 | hints.ai_socktype = SOCK_STREAM; 72 | 73 | /* get a linked list of appropriate addresses that we can bind to */ 74 | if((n = getaddrinfo(addr, port, &hints, &servinfo)) != 0) { 75 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(n)); 76 | return -1; 77 | } 78 | 79 | /* go through the linked list and bind to the first one we can */ 80 | for(p = servinfo; p; p = p->ai_next) { 81 | num_addrs++; 82 | 83 | if((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { 84 | perror("socket"); 85 | continue; 86 | } 87 | 88 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { 89 | perror("setsockopt"); 90 | /* we don't really care if this fails... */ 91 | } 92 | 93 | if(bind(fd, p->ai_addr, p->ai_addrlen) == -1) { 94 | close(fd); 95 | perror("bind"); 96 | continue; 97 | } 98 | 99 | break; 100 | } 101 | 102 | freeaddrinfo(servinfo); 103 | 104 | /* we reached the end of the servinfo list without successfully binding */ 105 | if(!p) { 106 | fprintf(stderr, "error: failed to bind to any of %d addresses\n", 107 | num_addrs); 108 | return -1; 109 | } 110 | 111 | if(listen(fd, SOMAXCONN) == -1) { 112 | perror("listen"); 113 | close(fd); 114 | return -1; 115 | } 116 | 117 | return fd; 118 | } 119 | 120 | /* callback for solve_letters to send a word to the client */ 121 | void send_word(const char *word, void *data) { 122 | int fd = *(int *)data; 123 | int *fd_ptr = data; 124 | char nl = '\n'; 125 | 126 | /* TODO: write in chunks if the full write does not happen at once */ 127 | 128 | if(write(fd, word, strlen(word)) < 0) { 129 | perror("write"); 130 | close(fd); 131 | *fd_ptr = -1; 132 | pthread_exit(NULL); 133 | } 134 | if(write(fd, &nl, 1) < 0) { 135 | perror("write"); 136 | close(fd); 137 | *fd_ptr = -1; 138 | pthread_exit(NULL); 139 | } 140 | } 141 | 142 | /* read letter sets from a client and solve them */ 143 | void *client_thread(void *arg) { 144 | int fd = *(int *)arg; 145 | char buf[1024]; 146 | char *p; 147 | size_t gotbytes = 0; 148 | int n; 149 | char nl = '\n'; 150 | 151 | while((n = read(fd, buf + gotbytes, 1024 - gotbytes)) > 0) { 152 | gotbytes += n; 153 | buf[gotbytes] = '\0'; 154 | 155 | while((p = strchr(buf, '\n'))) { 156 | *p = '\0'; 157 | 158 | solve_letters(buf, send_word, arg); 159 | 160 | if(write(fd, &nl, 1) < 0) 161 | goto disconnect; 162 | 163 | gotbytes -= p + 1 - buf; 164 | memmove(buf, p + 1, gotbytes); 165 | buf[gotbytes] = '\0'; 166 | } 167 | 168 | if(gotbytes >= 1024) { 169 | char *err = "!line too long\n"; 170 | write(fd, err, strlen(err)); 171 | goto disconnect; 172 | } 173 | } 174 | 175 | disconnect: 176 | free(arg); 177 | 178 | if(n == -1) { 179 | perror("read"); 180 | } 181 | 182 | close(fd); 183 | 184 | nclients--; 185 | 186 | return NULL; 187 | } 188 | 189 | /* make a thread to handle a client */ 190 | void handle_client(int fd) { 191 | pthread_t tid; 192 | int *fd_ptr = malloc(sizeof(int)); 193 | 194 | *fd_ptr = fd; 195 | 196 | if(pthread_create(&tid, NULL, client_thread, fd_ptr) != 0) { 197 | perror("pthread_create"); 198 | close(fd); 199 | return; 200 | } 201 | 202 | nclients++; 203 | } 204 | 205 | int main(int argc, char **argv) { 206 | int daemonize = 0; 207 | char *dict = DEFAULT_DICT; 208 | char *listenaddr = "localhost"; 209 | int maxclients = -1; 210 | char *port = "17220"; 211 | socklen_t sin_size; 212 | struct sockaddr_storage their_addr; 213 | struct sigaction sigpipe_handler; 214 | int fd; 215 | int c; 216 | 217 | opterr = 1; 218 | 219 | while((c = getopt_long(argc, argv, "Dd:hl:c:p:", opts, NULL)) != -1) { 220 | switch(c) { 221 | case 'D': daemonize = 1; break; 222 | case 'd': dict = optarg; break; 223 | case 'h': usage(); exit(0); break; 224 | case 'l': listenaddr = optarg; break; 225 | case 'c': maxclients = atoi(optarg); break; 226 | case 'p': port = optarg; break; 227 | default: exit(1); 228 | } 229 | } 230 | 231 | load_dictionary(dict, 0); 232 | 233 | /* listen on the given address and port */ 234 | if((fd = start_listening(listenaddr, port)) == -1) 235 | exit(1); 236 | 237 | if(daemonize && daemon(0, 0) == -1) 238 | perror("daemon"); 239 | 240 | /* warn about sigpipe */ 241 | memset(&sigpipe_handler, 0, sizeof(sigpipe_handler)); 242 | sigpipe_handler.sa_handler = sigpipe; 243 | sigemptyset(&sigpipe_handler.sa_mask); 244 | sigaction(SIGPIPE, NULL, &sigpipe_handler); 245 | 246 | /* repeatedly accept connections and deal with client letter sets */ 247 | while(1) { 248 | sin_size = sizeof(their_addr); 249 | int clientfd = accept(fd, (struct sockaddr *)&their_addr, &sin_size); 250 | 251 | if(clientfd != -1) { 252 | if(nclients < maxclients || maxclients < 0) { 253 | handle_client(clientfd); 254 | } else { 255 | char *err = "!too many clients\n"; 256 | fprintf(stderr, "warning: disconnecting client because there " 257 | "are too many\n"); 258 | write(clientfd, err, strlen(err)); 259 | close(clientfd); 260 | } 261 | } else { 262 | perror("accept"); 263 | exit(1); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /cntdn.js: -------------------------------------------------------------------------------- 1 | /* Javascript version of cntdn 2 | * 3 | * Countdown game solver 4 | * 5 | * James Stanley 2014 6 | */ 7 | 8 | function _recurse_solve_letters(letters, node, used_letter, cb, answer) { 9 | if (node[0]) 10 | cb(answer, node[0]); 11 | 12 | if (answer.length == letters.length) 13 | return; 14 | 15 | var done = {}; 16 | 17 | for (var i = 0; i < letters.length; i++) { 18 | var c = letters.charAt(i); 19 | 20 | if (used_letter[i] || done[c]) 21 | continue; 22 | 23 | if (node[c]) { 24 | used_letter[i] = true; 25 | done[c] = true; 26 | _recurse_solve_letters(letters, node[c], used_letter, cb, answer+c); 27 | used_letter[i] = false; 28 | } 29 | } 30 | } 31 | 32 | function solve_letters(letters, cb) { 33 | _recurse_solve_letters(letters, dictionary, {}, cb, ''); 34 | } 35 | 36 | function sufficient_letters(word, letters) { 37 | var count = {}; 38 | 39 | for (var i = 0; i < letters.length; i++) { 40 | if (!count[letters.charAt(i)]) 41 | count[letters.charAt(i)] = 0; 42 | count[letters.charAt(i)]++; 43 | } 44 | 45 | for (var i = 0; i < word.length; i++) { 46 | if (!count[word.charAt(i)]) 47 | return false; 48 | count[word.charAt(i)]--; 49 | if (count[word.charAt(i)] < 0) 50 | return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | function word_in_dictionary(word) { 57 | var node = dictionary; 58 | var idx = 0; 59 | 60 | while (idx < word.length) { 61 | node = node[word.charAt(idx)]; 62 | idx++; 63 | if (!node) 64 | return false; 65 | } 66 | 67 | if (!node[0]) 68 | return false; 69 | return true; 70 | } 71 | 72 | var bestdiff; 73 | var bestvalsums; 74 | 75 | var OPS = { 76 | "+": function(n1, n2) { if (n1 < 0 || n2 < 0) return false; return n1+n2; }, 77 | "-": function(n1, n2) { if (n2 >= n1) return false; return n1-n2; }, 78 | "_": function(n2, n1) { if (n2 >= n1) return false; return n1-n2; }, 79 | "*": function(n1, n2) { return n1*n2; }, 80 | "/": function(n1, n2) { if (n2 == 0 || n1%n2 != 0) return false; return n1/n2; }, 81 | "?": function(n2, n1) { if (n2 == 0 || n1%n2 != 0) return false; return n1/n2; }, 82 | }; 83 | 84 | var OPCOST = { 85 | "+": 1, 86 | "-": 1.05, 87 | "_": 1.05, 88 | "*": 1.2, 89 | "/": 1.3, 90 | "?": 1.3, 91 | }; 92 | 93 | function _recurse_solve_numbers(numbers, searchedi, was_generated, target, levels, valsums, trickshot) { 94 | levels--; 95 | 96 | for (var i = 0; i < numbers.length-1; i++) { 97 | var ni = numbers[i]; 98 | 99 | if (ni === false) 100 | continue; 101 | 102 | numbers[i] = false; 103 | 104 | for (var j = i+1; j < numbers.length; j++) { 105 | var nj = numbers[j]; 106 | 107 | if (nj === false) 108 | continue; 109 | 110 | if (i < searchedi && !was_generated[i] && !was_generated[j]) 111 | continue; 112 | 113 | for (var o in OPS) { 114 | var r = OPS[o](ni[0], nj[0]); 115 | if (r === false) 116 | continue; 117 | 118 | var op_cost = Math.abs(r); 119 | while (op_cost % 10 == 0 && op_cost != 0) 120 | op_cost /= 10; 121 | if ((ni[0] == 10 || nj[0] == 10) && o == '*') // HACK: multiplication by 10 is cheap 122 | op_cost = 1; 123 | op_cost *= OPCOST[o]; 124 | 125 | var newvalsums = valsums + op_cost; 126 | 127 | if ((Math.abs(r - target) < Math.abs(bestresult[0] - target)) 128 | || (Math.abs(r - target) == Math.abs(bestresult[0] - target) && (trickshot || newvalsums < bestvalsums))) { 129 | bestresult = [r,o,ni,nj]; 130 | bestvalsums = newvalsums; 131 | } 132 | 133 | numbers[j] = [r, o, ni, nj]; 134 | var old_was_gen = was_generated[j]; 135 | was_generated[j] = true; 136 | 137 | if (levels > 0 && (trickshot || bestresult[0] != target || newvalsums < bestvalsums)) 138 | _recurse_solve_numbers(numbers, i+1, was_generated, target, levels, newvalsums, trickshot); 139 | 140 | was_generated[j] = old_was_gen; 141 | numbers[j] = nj; 142 | } 143 | } 144 | 145 | numbers[i] = ni; 146 | } 147 | } 148 | 149 | function tidyup_result(result) { 150 | var mapping = { 151 | "?": "/", "_": "-" 152 | }; 153 | 154 | var swappable = { 155 | "*": true, "+": true 156 | }; 157 | 158 | if (result.length < 4) 159 | return result; 160 | 161 | for (var i = 2; i < result.length; i++) { 162 | var child = result[i]; 163 | 164 | child = tidyup_result(child); 165 | 166 | if (child[1] == result[1] && swappable[result[1]]) { 167 | result.splice(i--, 1); 168 | result = result.concat(child.slice(2)); 169 | } else { 170 | result[i] = child; 171 | } 172 | } 173 | 174 | if (result[1] in mapping) { 175 | result[1] = mapping[result[1]]; 176 | var j = result[2]; 177 | result[2] = result[3]; 178 | result[3] = j; 179 | } else if (swappable[result[1]]) { 180 | childs = result.slice(2).sort(function(a,b) { return b[0] - a[0]; }); 181 | for (var i = 2; i < result.length; i++) 182 | result[i] = childs[i-2]; 183 | } 184 | 185 | return result; 186 | } 187 | 188 | function fullsize(array) { 189 | if (array.constructor != Array) 190 | return 0; 191 | 192 | var l = 0; 193 | 194 | for (var i = 0; i < array.length; i++) 195 | l += fullsize(array[i]); 196 | 197 | return l + array.length; 198 | } 199 | 200 | function serialise_result(result) { 201 | var childparts = []; 202 | 203 | for (var i = 2; i < result.length; i++) { 204 | var child = result[i]; 205 | 206 | if (child.length >= 4) 207 | childparts.push(serialise_result(child)); 208 | } 209 | 210 | childparts = childparts.sort(function(a,b) { return fullsize(b) - fullsize(a); }); 211 | 212 | var parts = []; 213 | for (var i = 0; i < childparts.length; i++) { 214 | parts = parts.concat(childparts[i]); 215 | } 216 | 217 | var sliced = result.slice(2).map(function(l) { return l[0]; }); 218 | var thispart = [result[0], result[1]].concat(sliced); 219 | 220 | return parts.concat([thispart]); 221 | } 222 | 223 | function stringify_result(serialised, target) { 224 | var output = ''; 225 | 226 | serialised = serialised.slice(0); 227 | 228 | for (var i = 0; i < serialised.length; i++) { 229 | var x = serialised[i]; 230 | 231 | var args = x.slice(2); 232 | output += args.join(' ' + x[1] + ' ') + ' = ' + x[0] + '\n'; 233 | } 234 | 235 | var result = serialised[serialised.length-1][0]; 236 | if (result != target) 237 | output += '(off by ' + (Math.abs(result - target)) + ')\n'; 238 | 239 | return output; 240 | } 241 | 242 | function _solve_numbers(numbers, target, trickshot) { 243 | numbers = numbers.map(function(x) { return [x, false] }); 244 | 245 | var was_generated = []; 246 | for (var i = 0; i < numbers.length; i++) 247 | was_generated.push(false); 248 | 249 | bestresult = [0, 0]; 250 | 251 | /* attempt to solve with dfs */ 252 | _recurse_solve_numbers(numbers, 0, was_generated, target, numbers.length, 0, trickshot); 253 | 254 | return bestresult; 255 | } 256 | 257 | function solve_numbers(numbers, target, trickshot) { 258 | numbers.sort(); 259 | bestresult = [numbers[0], numbers[0]]; 260 | 261 | /* see if one of these numbers is the answer; with trickshot you'd rather 262 | * have an interesting answer that's close than an exact answer 263 | */ 264 | if (!trickshot) { 265 | for (var i = 1; i < numbers.length; i++) { 266 | if (Math.abs(numbers[i] - target) < Math.abs(bestresult[0] - target)) { 267 | bestresult = [numbers[i], numbers[i]]; 268 | bestvalsums = numbers[i]; 269 | } 270 | } 271 | if (bestresult[0] == target) 272 | return target + " = " + target; 273 | } 274 | 275 | return stringify_result(serialise_result(tidyup_result(_solve_numbers(numbers, target, trickshot))), target); 276 | } 277 | --------------------------------------------------------------------------------