Created
September 6, 2023 15:57
-
-
Save olaugh/7fca18a0bccbec88d2f02e8c126fe39f 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
#!/usr/bin/perl -w | |
# JumbleTime-style quiz program. Copyright(C) Amit Chakrabarti, 2003-2007. | |
# Last updated Tue Feb 6 18:44:53 EST 2007. | |
# | |
# Modifying this program is allowed. If you add any interesting features | |
# please notify me by email: <amitc at ias dot edu>. I will try to keep | |
# a "latest" version of this program at the following URL: | |
# http://www.cs.princeton.edu/~amitc/Scrab/jt | |
# | |
# Functionality: Display a bunch of alphagrams on the screen in a grid, | |
# wait for user to solve them and type the words in, erasing alphagrams | |
# as they get fully solved. | |
# | |
# Input file must have lines of the following format: | |
# aenorst ATONERS SENATOR TREASON - | |
# aeinort OTARINE + | |
# and it is very important that the alphagram be lowercase! The final | |
# +/- on the line indicates question seen before (+) or new (-). This | |
# program is not robust, so the input file had better be well behaved; | |
# thus, no duplicate entries, no blank lines, no funny stuff. | |
# | |
# Inspired by the applet at http://www.jumbletime.com; try that out too! | |
use strict; | |
use Term::ANSIScreen qw(:all); | |
die "USAGE: jt <inputfile> [<duration> <requiz_prob>]\n" if $#ARGV < 0; | |
my ($num_qs, $quiz_duration) = (64, 200); # 50 questions, 300 seconds | |
my $requiz_prob = 0.0; # Prob to redo a seen question | |
my ($numcols, $width, $height) = (8, 11, 2); # Display parameters | |
my (@words, @picked, @counts, $i, $lineno, $ttl); | |
my $filename = shift; | |
$quiz_duration = shift if defined $ARGV[0]; | |
$requiz_prob = shift if defined $ARGV[0]; | |
# ---> Read the words and select a random subset for the quiz. <--- | |
open T, $filename or die "Cannot open $filename: $!"; | |
@words = <T>; | |
@words = map {[ split ' ' ]} grep !/^#/, @words; | |
cls; | |
for(($i,$lineno,$ttl) = (0,0,100*$#words); $i < $num_qs && $ttl; $ttl--) { | |
# If $requiz_prob negative, pick next free word, else a random word | |
my $rnd = $requiz_prob < 0 ? $lineno++ : int rand @words; | |
next if join(' ', @picked) =~ /\b$rnd\b/; | |
next if $words[$rnd][-1] =~ /\+/ && rand > $requiz_prob; | |
push @picked, $rnd; | |
push @counts, $#{$words[$rnd]} - 1; | |
update($i++); | |
} | |
$num_qs = $i; # Needed in case we quit the loop due to $ttl | |
close T; | |
unless($num_qs) { | |
print "Couldn't find anything unsolved.\n"; | |
exit 0; | |
} | |
# ---> Do the quiz itself. <--- | |
my $promptrow = 1 + (1 + int($num_qs / $numcols)) * $height; | |
my $num_unsolved = $num_qs; | |
eval { | |
local $SIG{ALRM} = sub { die "alarm\n" }; | |
alarm $quiz_duration; | |
do { | |
my $resp = getresp(); | |
die "alarm\n" if $resp eq "XXX"; # Fake timeout if user entered 'XXX' | |
for(my ($i,$found)=(0,0); $i < $num_qs && !$found; $i++) { | |
if(join(' ', @{$words[$picked[$i]]}) =~ /\b$resp#?\b/) { | |
$found = 1; | |
$words[$picked[$i]] = [ map {/$resp/?lc:$_} @{$words[$picked[$i]]} ]; | |
if(--$counts[$i] == 0) | |
{ erase($i); $num_unsolved--; } | |
else | |
{ update($i); } | |
} | |
} | |
} while $num_unsolved; | |
}; | |
# ---> Quiz over; either we timed out or all questions were solved. <--- | |
if($@) { | |
die "Something bad happened at '$@'\n" unless $@ eq "alarm\n"; | |
print locate($promptrow,0), clline, | |
"Time's up! You missed $num_unsolved/$num_qs:\n"; | |
for(my $i=0; $i<$num_qs; $i++) { | |
print join(' ', @{$words[$picked[$i]]}), "\n" if $counts[$i]; | |
} | |
} | |
else { | |
my $timeleft = alarm 0; | |
print locate($promptrow,0), clline, "Done!\n$timeleft seconds to spare.\n"; | |
} | |
# ---> Update the JT file marking questions solved this session. <--- | |
open T, ">$filename" or die "Cannot write to $filename: $!"; | |
my $idx = 0; | |
my %indexof = map { $_ => $idx++ } @picked; | |
for my $j (0 .. $#words) { | |
print T shift @{$words[$j]}, " "; | |
my $line = uc join(' ', @{$words[$j]}); | |
if(exists $indexof{$j}) { | |
$line =~ s/\+/-/ if $counts[$indexof{$j}]; # Mark '-' if unsolved | |
$line =~ s/-/+/ if !$counts[$indexof{$j}]; # Mark '+' if solved | |
} | |
print T "$line\n"; | |
} | |
close T; | |
0; | |
# ---> Accept keyboard input. <--- | |
sub getresp { | |
my $line; | |
do { | |
print locate($promptrow,0), clline, "Solution: "; | |
$line = <STDIN>; | |
$line =~ s/[\-+\s\e\[\]]//g; | |
} while $line eq ""; | |
chomp $line; | |
uc $line; | |
} | |
# ---> Erase a solved question. <--- | |
sub erase { | |
use integer; | |
savepos; | |
my $i = shift; | |
my $row = 1 + ($i / $numcols) * $height; | |
my $col = 1 + ($i % $numcols) * $width; | |
print locate($row,$col), ' ' x $width; | |
loadpos; | |
} | |
# ---> Print a question (alphagram), updating #anagrams left to solve. <--- | |
sub update { | |
use integer; | |
savepos; | |
my $i = shift; | |
my $row = 1 + ($i / $numcols) * $height; | |
my $col = 1 + ($i % $numcols) * $width; | |
my $mult = $counts[$i] > 1 ? " $counts[$i]" : " "; | |
print locate($row,$col), uc $words[$picked[$i]][0], RED, $mult, RESET; | |
loadpos; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment