Just another update

This commit is contained in:
Ingy döt Net 2015-02-20 00:35:01 -05:00
parent a25938f123
commit 00a190b0a6
6591 changed files with 94363 additions and 23227 deletions

View file

@ -1,6 +1,7 @@
The [[wp:24 Game|24 Game]] tests one's mental arithmetic.
Write a program that [[task feature::Rosetta Code:randomness|randomly]] chooses and [[task feature::Rosetta Code:user output|displays]] four digits, each from one to nine, with repetitions allowed. The program should prompt for the player to enter an arithmetic expression using ''just'' those, and ''all'' of those four digits. The program should ''check'' then [[task feature::Rosetta Code:parsing|evaluate the expression]]. The goal is for the player to [[task feature::Rosetta Code:user input|enter]] an expression that evaluates to '''24'''.
Write a program that [[task feature::Rosetta Code:randomness|randomly]] chooses and [[task feature::Rosetta Code:user output|displays]] four digits, each from one to nine, with repetitions allowed. The program should prompt for the player to enter an arithmetic expression using ''just'' those, and ''all'' of those four digits, used exactly ''once'' each. The program should ''check'' then [[task feature::Rosetta Code:parsing|evaluate the expression]].
The goal is for the player to [[task feature::Rosetta Code:user input|enter]] an expression that evaluates to '''24'''.
* Only multiplication, division, addition, and subtraction operators/functions are allowed.
* Division should use floating point or rational arithmetic, etc, to preserve remainders.
* Brackets are allowed, if using an infix expression evaluator.

View file

@ -0,0 +1,83 @@
@echo off
::24.bat
::
::Batch file implemetnation of the 24 Game where a player is given four random
::digits n, where 1 <= n <= 9, and needs to provide a simple arithmetic
::operation that does evaluate to 24.
::
::Note: [1]This implementation does not evaluate brackets
:: [2]This implementation does not keep remainders since batch language
:: has no support for floating point calculations
cls
echo The 24 Game
echo.
echo Given four digits, provide a simple arithmetic expression
echo that evaluates to 24 using +,-,*,/.
echo.
echo Enter 'SHOW' to show the digits or 'EXIT' to end the game.
echo.
set TRY=0
::Get four random digits
set /a "DIGIT_1=%RANDOM% %%9 + 1"
set /a "DIGIT_2=%RANDOM% %%9 + 1"
set /a "DIGIT_3=%RANDOM% %%9 + 1"
set /a "DIGIT_4=%RANDOM% %%9 + 1"
goto SHOW
::Main Program Loop
:MAIN
set /a TRY+=1
set "TMP_DIGIT_1=%DIGIT_1%"
set "TMP_DIGIT_2=%DIGIT_2%"
set "TMP_DIGIT_3=%DIGIT_3%"
set "TMP_DIGIT_4=%DIGIT_4%"
::Promt for an answer and trim answer string
set /p ANSWER="Try %TRY%: "
set "ANSWER=%ANSWER: =%"
if /i "%ANSWER%"=="SHOW" goto SHOW
if /i "%ANSWER%"=="EXIT" goto ABORT
if "%ANSWER:~6,1%"=="" goto ERROR_MISSING_CHARS:
::Determine if each digits has ben used once in the input equation
set DIGITS_USED=0
set COUNTER=0
:LOOP
call set CURR_DIGIT=%%ANSWER:~%COUNTER%,1%%
if %CURR_DIGIT%==%TMP_DIGIT_1% (set "TMP_DIGIT_1=" & set /a "DIGITS_USED+=1")
if %CURR_DIGIT%==%TMP_DIGIT_2% (set "TMP_DIGIT_2=" & set /a "DIGITS_USED+=2")
if %CURR_DIGIT%==%TMP_DIGIT_3% (set "TMP_DIGIT_3=" & set /a "DIGITS_USED+=4")
if %CURR_DIGIT%==%TMP_DIGIT_4% (set "TMP_DIGIT_4=" & set /a "DIGITS_USED+=8")
set /a "COUNTER+=2"
if not "%COUNTER%"=="8" goto LOOP
if not "%DIGITS_USED%"=="15" goto ERROR_INCORRECT_INPUT
::Calculate and evaluate result
set /a "RESULT=%ANSWER%"
if "%RESULT%"=="24" goto END
echo Invalid input [Bad result: Expected 24, Received %RESULT%]
goto MAIN
:ERROR_MISSING_CHARS
echo Invalid input [insufficient number of characters]
goto MAIN
:ERROR_INCORRECT_INPUT
echo Invalid input [incorrect digits]
goto MAIN
:SHOW
echo Given digits: %DIGIT_1% %DIGIT_2% %DIGIT_3% %DIGIT_4%
goto MAIN
:END
echo Correct input [Congratulations!]
:ABORT
echo.

View file

@ -9,7 +9,7 @@
chosen-digits)
(read))
(lose () (error 'bad-equation))
(choose () (setf chosen-digits (loop repeat 4 collecting (random 10))))
(choose () (setf chosen-digits (loop repeat 4 collecting (1+ (random 9)))))
(check (e)
(typecase e
((eql bye) (return-from 24-game))

View file

@ -0,0 +1,62 @@
import java.util.*;
public class Game24 {
public static void main(String[] args) {
int[] digits = randomDigits();
Scanner in = new Scanner(System.in);
System.out.print("Make 24 using these digits: ");
System.out.println(Arrays.toString(digits));
System.out.print("> ");
Stack<Float> s = new Stack<>();
long total = 0;
for (char c : in.nextLine().toCharArray()) {
if ('0' <= c && c <= '9') {
int d = c - '0';
total += (1 << (d * 5));
s.push((float) d);
} else if ("+/-*".indexOf(c) != -1) {
s.push(applyOperator(s.pop(), s.pop(), c));
}
}
if (tallyDigits(digits) != total)
System.out.print("Not the same digits. ");
else if (Math.abs(24 - s.peek()) < 0.001F)
System.out.println("Correct!");
else
System.out.print("Not correct.");
}
static float applyOperator(float a, float b, char c) {
switch (c) {
case '+':
return a + b;
case '-':
return b - a;
case '*':
return a * b;
case '/':
return b / a;
default:
return Float.NaN;
}
}
static long tallyDigits(int[] a) {
long total = 0;
for (int i = 0; i < 4; i++)
total += (1 << (a[i] * 5));
return total;
}
static int[] randomDigits() {
Random r = new Random();
int[] result = new int[4];
for (int i = 0; i < 4; i++)
result[i] = r.nextInt(9) + 1;
return result;
}
}

View file

@ -1,48 +1,51 @@
String.prototype.replaceAll = function(patt,repl) { var that = this;
that = that.replace(patt,repl);
if (that.search(patt) != -1) {
that = that.replaceAll(patt,repl);
}
return that;
};
function twentyfour(numbers, input) {
var invalidChars = /[^\d\+\*\/\s-\(\)]/;
function validChars(input) { var regInvalidChar = /[^\d\+\*\/\s-\(\)]/;
return input.search(regInvalidChar) == -1;
}
var validNums = function(str) {
// Create a duplicate of our input numbers, so that
// both lists will be sorted.
var mnums = numbers.slice();
mnums.sort();
function validNums(str, nums) {
var arr, l;
arr = str.replaceAll(/[^\d\s]/," ").replaceAll(" "," ").trim().split(" ").sort();
l = arr.length;
// Sort after mapping to numbers, to make comparisons valid.
return str.replace(/[^\d\s]/g, " ")
.trim()
.split(/\s+/)
.map(function(n) { return parseInt(n, 10); })
.sort()
.every(function(v, i) { return v === mnums[i]; });
};
while(l--) { arr[l] = Number(arr[l]); }
var validEval = function(input) {
try {
return eval(input);
} catch (e) {
return {error: e.toString()};
}
};
return _.isEqual(arr,nums.sort());
if (input.trim() === "") return "You must enter a value.";
if (input.match(invalidChars)) return "Invalid chars used, try again. Use only:\n + - * / ( )";
if (!validNums(input)) return "Wrong numbers used, try again.";
var calc = validEval(input);
if (typeof calc !== 'number') return "That is not a valid input; please try again.";
if (calc !== 24) return "Wrong answer: " + String(calc) + "; please try again.";
return input + " == 24. Congratulations!";
};
// I/O below.
while (true) {
var numbers = [1, 2, 3, 4].map(function() {
return Math.floor(Math.random() * 8 + 1);
});
var input = prompt(
"Your numbers are:\n" + numbers.join(" ") +
"\nEnter expression. (use only + - * / and parens).\n", +"'x' to exit.", "");
if (input === 'x') {
break;
}
alert(twentyfour(numbers, input));
}
function validEval(input) { try { eval(input); } catch (e) { return false; };
return true;
}
var input;
while(true){ var numbers = [];
var i = 4;
while(i--) { numbers.push(Math.floor(Math.random()*8+1));
}
input = prompt("Your numbers are:\n"
+ numbers.join(" ")
+ "\nEnter expression. (use only + - * / and parens).\n"
+ "'x' to exit."
);
if (input === 'x') break;
!validChars(input) ? alert("Invalid chars used, try again. Use only:\n + - * / ( )")
: !validNums(input,numbers) ? alert("Wrong numbers used, try again.")
: !validEval(input) ? alert("Could not evaluate input, try again.")
: eval(input) != 24 ? alert("Wrong answer:" + eval(input) + "\nTry again.")
: alert(input + "== 24. Congrats!!")
;
}

View file

@ -1,63 +1,28 @@
validexpr(ex::Expr) = ex.head == :call && ex.args[1] in [:*,:/,:+,:-] && all(validexpr, ex.args[2:end])
validexpr(ex::Int) = true
validexpr(ex::Any) = false
findnumbers(ex::Number) = Int[ex]
findnumbers(ex::Expr) = vcat(map(findnumbers, ex.args)...)
findnumbers(ex::Any) = Int[]
function twentyfour()
function check(input)
d = ref(Int)
for i in input.args
if typeof(i) == Expr
c = check(i)
typeof(c) == String || append!(d,c)
elseif contains([:*,:/,:-,:+],i)
continue
elseif contains([1:9],i)
push!(d,i)
continue
elseif i > 9 || i < 1
d = "Sorry, $i is not allowed, please use only numbers between 1 and 9"
else
d = "Sorry, $i isn't allowed"
end
end
return d
end
new_digits() = [rand(1:9),rand(1:9),rand(1:9),rand(1:9)]
answer = new_digits()
print("The 24 Game\nYou will be given any four digits in the range 1 to 9, which may have repetitions.\n
Using just the +, -, *, and / operators show how to make an answer of 24.\n
Use parentheses, (), to ensure proper order of evaulation.\n
Enter 'n' fDouble>()
while( scanner.hasNext() ) {
if( scanner.hasNextInt() ) {
var n = scanner.nextInt()
// Make sure they're allowed to use n
if( n or a new set of digits, and 'q' to quit. Good luck!\n
Here's your first 4 digits\n$(answer[1]) $(answer[2]) $(answer[3]) $(answer[4])\n
>")
while true
input = chomp(readline(STDIN))
input == "q" && break
if input == "n"
answer = new_digits()
print("\nLet's try again, go ahead and guess\n Here's your 4 digits\n$(answer[1]) $(answer[2]) $(answer[3]) $(answer[4])\n>")
continue
end
input = try
parse(input)
catch
print("I couldn't calculate your answer, please try again\n>");
continue
end
c = check(input)
cc = all([sum(i .== answer) == sum(i .== c) for i in answer])
!cc && (print("Sorry, valid digits are \n$(answer[1]) $(answer[2]) $(answer[3]) $(answer[4])\n are allowed and all four must be used. Please try again\n>"); continue)
if eval(input) == 24
answer = new_digits()
print("\nYou did it!\nLet's do another round, or enter 'q' to quit\n
Here's your 4 digits\n$(answer[1]) $(answer[2]) $(answer[3]) $(answer[4])\n
>")
continue
else
print("\nSorry your answer calculates to $(eval(input))\nTry again\n>")
end
end
digits = sort!(rand(1:9, 4))
while true
print("enter expression using $digits => ")
ex = parse(readline())
try
validexpr(ex) || error("only *, /, +, - of integers is allowed")
nums = sort!(findnumbers(ex))
nums == digits || error("expression $ex used numbers $nums != $digits")
val = eval(ex)
val == 24 || error("expression $ex evaluated to $val, not 24")
println("you won!")
return
catch e
if isa(e, ErrorException)
println("incorrect: ", e.msg)
else
rethrow()
end
end
end
end

View file

@ -1,38 +1,23 @@
say "Here are your digits: ",
constant @digits = (1..9).roll(4)».Str;
grammar Exp24 {
token TOP { ^ <exp> $ }
token exp { <term> [ <op> <term> ]* }
token term { '(' <exp> ')' | \d }
token op { '+' | '-' | '*' | '/' }
token TOP { ^ <exp> $ { fail unless EVAL($/) == 24 } }
rule exp { <term>+ % <op> }
rule term { '(' <exp> ')' | <@digits> }
token op { < + - * / > }
}
my @digits = roll 4, 1..9; # to a gamer, that's a "4d9" roll
say "Here's your digits: {@digits}";
while my $exp = prompt "\n24-Exp? " {
unless is-valid($exp, @digits) {
say "Sorry, your expression is not valid!";
next;
while my $exp = prompt "\n24? " {
if Exp24.parse: $exp {
say "You win :)";
last;
} else {
say pick 1,
'Sorry. Try again.' xx 20,
'Try harder.' xx 5,
'Nope. Not even close.' xx 2,
'Are you five or something?',
'Come on, you can do better than that.';
}
my $value = eval $exp;
say "$exp = $value";
if $value == 24 {
say "You win!";
last;
}
say "Sorry, your expression doesn't evaluate to 24!";
}
sub is-valid($exp, @digits) {
unless ?Exp24.parse($exp) {
say "Expression doesn't match rules!";
return False;
}
unless $exp.comb(/\d/).sort.join == @digits.sort.join {
say "Expression must contain digits {@digits} only!";
return False;
}
return True;
}

View file

@ -1,122 +1,46 @@
#!/usr/bin/perl
use strict;
#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';
print <<'EOF';
The 24 Game
Given any four digits in the range 1 to 9, which may have repetitions,
Using just the +, -, *, and / operators; and the possible use of
brackets, (), show how to make an answer of 24.
parentheses, (), show how to make an answer of 24.
An answer of "q" will quit the game.
An answer of "!" will generate a new set of four digits.
Otherwise you are repeatedly asked for an expression until it evaluates to 24
An answer of "q" or EOF will quit the game.
A blank answer will generate a new set of four digits.
Otherwise you are repeatedly asked for an expression until it evaluates to 24.
Note: you cannot form multiple digit numbers from the supplied digits,
so an answer of 12+12 when given 1, 2, 2, and 1 would not be allowed.
EOF
while(1)
{
my $iteration_num = 0;
my $try = 1;
while (1) {
my @digits = map { 1+int(rand(9)) } 1..4;
say "\nYour four digits: ", join(" ", @digits);
print "Expression (try ", $try++, "): ";
my $numbers = make_numbers();
my $entry = <>;
if (!defined $entry || $entry eq 'q')
{ say "Goodbye. Sorry you couldn't win."; last; }
$entry =~ s/\s+//g; # remove all white space
next if $entry eq '';
TRY_SOLVING:
while(1)
{
$iteration_num++;
print "Expression ${iteration_num}: ";
my $given_digits = join "", sort @digits;
my $entry_digits = join "", sort grep { /\d/ } split(//, $entry);
if ($given_digits ne $entry_digits || # not correct digits
$entry =~ /\d\d/ || # combined digits
$entry =~ m|[-+*/]{2}| || # combined operators
$entry =~ tr|-0-9()+*/||c) # Invalid characters
{ say "That's not valid"; next; }
my $entry = <>;
chomp($entry);
my $n = eval $entry;
last TRY_SOLVING if $entry eq '!';
exit if $entry eq 'q';
my $result = play($numbers, $entry);
if (!defined $result)
{
print "That's not valid\n";
next TRY_SOLVING;
}
elsif ($result != 24)
{
print "Sorry, that's $result\n";
next TRY_SOLVING;
}
else
{
print "That's right! 24!!\n";
exit;
}
}
}
sub make_numbers
{
my %numbers = ();
print "Your four digits:";
for(1..4)
{
my $i = 1 + int(rand(9));
$numbers{$i}++;
print "$i ";
}
print "\n";
return \%numbers;
}
sub play
{
my ($numbers, $expression) = @_;
my %running_numbers = %$numbers;
my @chars = split //, $expression;
my $operator = 1;
CHARS:
foreach (@chars)
{
next CHARS if $_ =~ /[()]/;
$operator = !$operator;
if (! $operator)
{
if (defined $running_numbers{$_} && $running_numbers{$_} > 0)
{
$running_numbers{$_}--;
next CHARS;
}
else
{
return;
}
}
else
{
return if $_ !~ m{[-+*/]};
}
}
foreach my $remaining (values(%running_numbers))
{
if ($remaining > 0)
{
return;
}
}
return eval($expression);
if (!defined $n) { say "Invalid expression"; }
elsif ($n == 24) { say "You win!"; last; }
else { say "Sorry, your expression is $n, not 24"; }
}

View file

@ -0,0 +1,30 @@
:- initialization(main).
answer(24).
play :- round, play ; true.
round :-
prompt(Ns), get_line(Input), Input \= "stop"
, ( phrase(parse(Ns,[]), Input) -> Result = 'correct'
; Result = 'wrong'
), write(Result), nl, nl
. % where
prompt(Ns) :- length(Ns,4), maplist(random(1,10), Ns)
, write('Digits: '), write(Ns), nl
.
parse([],[X]) --> { answer(X) }.
parse(Ns,[Y,X|S]) --> "+", { Z is X + Y }, parse(Ns,[Z|S]).
parse(Ns,[Y,X|S]) --> "-", { Z is X - Y }, parse(Ns,[Z|S]).
parse(Ns,[Y,X|S]) --> "*", { Z is X * Y }, parse(Ns,[Z|S]).
parse(Ns,[Y,X|S]) --> "/", { Z is X div Y }, parse(Ns,[Z|S]).
parse(Ns,Stack) --> " ", parse(Ns,Stack).
parse(Ns,Stack) --> { select(N,Ns,Ns1), number_codes(N,[Code]) }
, [Code], parse(Ns1,[N|Stack])
.
get_line(Xs) :- get_code(X)
, ( X == 10 -> Xs = [] ; Xs = [X|Ys], get_line(Ys) )
.
main :- randomize, play, halt.

View file

@ -1,204 +1,22 @@
/*REXX program which allows a user to play the game of 24 (twenty-four).*/
/*
Argument is either of three forms: (blank)
ssss
ssss-ffff
where one or both strings must be exactly four numerals (digits)
comprised soley of the numerals (digits) 1 > 9 (no zeroes).
SSSS is the start,
FFFF is the start.
If no argument is specified, this program finds a four digit
number (no zeroes) which has at least one solution, and shows
the number to the user, requesting that they enter a solution
in the form of: w operator x operator y operator z
where w x y and z are single digit numbers (no zeroes),
and operator can be any one of: + - * /
Parentheses () may be used in the normal manner for grouping,
as well as brackets [] or braces {}.
*/
parse arg orig /*get the guess from the argument. */
orig=space(orig,0) /*remove extraneous blanks from ORIG. */
parse var orig start '-' finish /*get the start and finish (maybe). */
finish=word(finish start,1) /*if no FINISH specified, use START.*/
opers='+-*/' /*define the legal arithmetic operators*/
ops=length(opers) /* ... and the count of them (length). */
groupsymbols='()[]{}' /*legal grouping symbols. */
indent=left('',30) /*used to indent display of solutions. */
Lpar='(' /*a string to make REXX code prettier. */
Rpar=')' /*ditto. */
show=1 /*flag used show solutions (0 = not). */
digs=123456789 /*numerals (digits) that can be used. */
do j=1 for ops /*define a version for fast execution. */
o.j=substr(opers,j,1)
end /*j*/
if orig\=='' then do
sols=solve(start,finish)
if sols<0 then exit 13
if sols==0 then sols='No' /*un-geek the SOLS var.*/
say; say sols 'unique solution's(finds) "found for" orig
exit /* [↑] "S" pluralizes.*/
end
show=0 /*stop SOLVE from blabbing solutions. */
do forever
rrrr=random(1111,9999)
if pos(0,rrrr)\==0 then iterate
if solve(rrrr)\==0 then leave
end /*forever*/
show=1 /*enable SOLVE to show solutions. */
rrrr=sort(rrrr) /*sort four elements. */
rd.=0
do j=1 for 9 /*digit count # for each digit in RRRR.*/
_=substr(rrrr,j,1)
rd._=countdigs(rrrr,_)
end /*j*/
do guesses=1; say
say 'Using the digits' rrrr", enter an expression that equals 24 (or QUIT):"
pull y; y=space(y,0) /*get Y from CL, then remove all blanks*/
if y=='QUIT' then exit /*Does user want to quit? If so, exit.*/
_v=verify(y, digs || opers || groupsymbols); __=substr(y, _v, 1)
if _v\==0 then do; call ger 'invalid character:' __; iterate; end
if pos('**',y) then do; call ger 'invalid ** operator'; iterate; end
if pos('//',y) then do; call ger 'invalid // operator'; iterate; end
yL=length(y)
if y=='' then do; call validate y; iterate; end
do j=1 for yL-1; if \datatype(substr(y, j , 1), 'W') then iterate
if \datatype(substr(y, j+1, 1), 'W') then iterate
call ger 'invalid use of digit abuttal'
iterate guesses
end /*j*/
yd=countdigs(y,digs) /*count of digits 123456789.*/
if yd<4 then do
call ger 'not enough digits entered.'
iterate guesses
end
if yd>4 then do
call ger 'too many digits entered.'
iterate guesses
end
do j=1 for 9; if rd.j==0 then iterate
_d=countdigs(y,j)
if _d==rd.j then iterate
if _d<rd.j then call ger 'not enough' j "digits, must be" rd.j
else call ger 'too many' j "digits, must be" rd.j
iterate guesses
end /*j*/
y=translate(y,'()()',"[]{}")
signal on syntax
interpret 'ans='y; ans=ans/1
if ans==24 then leave guesses
say 'incorrect,' y'='ans
end /*guesses*/
say
say center('', 79)
say center(' ', 79)
say center(' congratulations ! ', 79)
say center(' ', 79)
say center('', 79)
exit
/*───────────────────────────SOLVE subroutine───────────────────────────*/
solve: parse arg ssss,ffff /*parse the argument passed to SOLVE. */
if ffff=='' then ffff=ssss /*create a FFFF if necessary. */
if \validate(ssss) then return -1
if \validate(ffff) then return -1
finds=0 /*number of found solutions (so far). */
x.=0 /*a method to hold unique expressions. */
/*alternative: indent=copies(' ',30) */
do g=ssss to ffff /*process a (possible) range of values.*/
if pos(0,g)\==0 then iterate /*ignore values with zero in them. */
do j=1 for 4 /*define a version for fast execution. */
g.j=substr(g,j,1)
end /*j*/
do i =1 for ops /*insert an operator after 1st number. */
do j =1 for ops /*insert an operator after 2nd number. */
do k =1 for ops /*insert an operator after 2nd number. */
do m=0 to 4-1; L.="" /*assume no left parenthesis so far. */
do n=m+1 to 4 /*match left paren with a right paren. */
L.m=Lpar /*define a left paren, m=0 means ignore*/
R.="" /*un-define all right parenthesis. */
if m==1 & n==2 then L.="" /*special case: (n)+ ... */
else if m\==0 then R.n=Rpar /*no (, no )*/
e = L.1 g.1 o.i L.2 g.2 o.j L.3 g.3 R.3 o.k g.4 R.4
e=space(e,0) /*remove all blanks from the expression*/
/*(below) change expression: */
/* /(yyy) ===> /div(yyy) */
/*Enables to check for division by zero*/
origE=e /*keep old version for the display. */
if pos('/(',e)\==0 then e=changestr('/(',e,"/div(")
/*The above could be replaced by: */
/* e=changestr('/(',e,"/div(") */
/*INTERPRET stresses REXX's groin, */
/*so try to avoid repeated lifting. */
if x.e then iterate /*was the expression already used? */
x.e=1 /*mark this expression as unique. */
/*have REXX do the heavy lifting. */
interpret 'x=' e
x=x/1 /*remove trailing decimal points. */
if x\==24 then iterate /*Not correct? Try again.*/
finds=finds+1 /*bump number of found solutions. */
_=translate(origE, '][', ")(") /*show [], not ().*/
if show then say indent 'a solution:' _ /*show solution.*/
end /*n*/
end /*m*/
end /*k*/
end /*j*/
end /*i*/
end /*g*/
return finds
/*───────────────────────────CHANGESTR subroutine───────────────────────*/
changestr: procedure; parse arg o,h,n,,r; w=length(o); if w==0 then return n||h
do forever; parse var h y (o) _ +(w) h; if _=='' then return r||y; r=r||y||n; end
/*───────────────────────────COUNTDIGS subroutine───────────────────────*/
countdigs: arg field,numerals /*count of digits NUMERALS.*/
return length(field)-length(space(translate(field,,numerals),0))
/*───────────────────────────DIV subroutine─────────────────────────────*/
div: procedure; parse arg q /*tests if dividing by 0 (zero). */
if q=0 then q=1e9 /*if dividing by zero, change divisor. */
return q /*changing Q invalidates the expression*/
/*───────────────────────────GER subroutine─────────────────────────────*/
ger: say; say '*** error! *** for argument:' y; say arg(1); say; errCode=1; return 0
/*───────────────────────────S subroutine───────────────────────────────*/
s: if arg(1)==1 then return ''; return 's' /*simple pluralizer.*/
/*───────────────────────────SORT subroutine────────────────────────────*/
sort: procedure; arg nnnn; L=length(nnnn)
do i=1 for L /*build an array of digits from NNNN.*/
a.i=substr(nnnn,i,1) /*this enables SORT to sort an array. */
end /*i*/
do j=1 for L; _=a.j
do k=j+1 to L
if a.k<_ then parse value a.j a.k with a.k a.j
end /*k*/
end /*j*/
return a.1 || a.2 || a.3 || a.4
/*───────────────────────────SYNTAX subroutin───────────────────────────*/
syntax: call ger 'illegal syntax in' y; exit
/*───────────────────────────validate subroutine────────────────────────*/
validate: parse arg y; errCode=0; _v=verify(y,digs)
select
when y=='' then call ger 'no digits entered.'
when length(y)<4 then call ger 'not enough digits entered, must be 4'
when length(y)>4 then call ger 'too many digits entered, must be 4'
when pos(0,y)\==0 then call ger "can't use the digit 0 (zero)"
when _v\==0 then call ger 'illegal character:' substr(y,_v,1)
otherwise nop
end /*select*/
return \errCode
Argument for the "24" program is either of three forms: (blank)
ssss
ssss-ffff
where one or both strings must be exactly four numerals (digits)
comprised soley of the numerals (digits) 1 9 (with no zeroes).
SSSS is the start,
FFFF is the finish.
If no argument is specified, this program finds a four digit number with
no zeroes) which has at least one solution, and shows the number to
the user, requesting that they enter a solution in the form of:
w operator x operator y operator z
where w x y and z are single digit numbers (no zeroes).
and operator can be any one of: + - * /
Parentheses ( ), brackets [ ], and/or braces { } may be used in the
normal manner for grouping expressions. Leading signs are permitted.

View file

@ -1,25 +1,158 @@
changestr: Procedure
/* change needle to newneedle in haystack (as often as specified */
/* or all of them if count is omitted */
Parse Arg needle,haystack,newneedle,count
If count>'' Then Do
If count=0 Then Do
Say 'chstr count must be > 0'
Signal Syntax
End
End
res=""
changes=0
px=1
do Until py=0
py=pos(needle,haystack,px)
if py>0 then Do
res=res||substr(haystack,px,py-px)||newneedle
px=py+length(needle)
changes=changes+1
If count>'' Then
If changes=count Then Leave
End
end
res=res||substr(haystack,px)
Return res
/*REXX program which allows a user to play the game of 24 (twenty-four). */
numeric digits 15 /*allow more leeway when computing #s. */
parse arg yyy /*get the optional arguments from C.L. */
yyy = space(yyy,0) /*remove extraneous blanks from YYY. */
parse var yyy start '-' fin /*get the START and FINish (maybe). */
fin = word(fin start,1) /*if no FINish specified, use START.*/
opers = '+-*/' /*define the legal arithmetic operators*/
ops = length(opers) /* ··· and the count of them (length). */
groupSymbols = '()[]{}' /*legal grouping symbols for this game.*/
indent = left('',30) /*used to indent display of solutions. */
Lpar = '(' /*a string to make the output prettier.*/
Rpar = ')' /*Ditto. [You can say that again.] */
digs = 123456789 /*numerals (digits) that can be used. */
show = 1 /*flag used show solutions (0 = not). */
do j=1 for ops /*define a version for fast execution. */
@.j=substr(opers,j,1) /*assign each operation to an array. */
end /*j*/
signal on syntax /*enable program to trap syntax errors.*/
if yyy\=='' then do /*if START (or FINish), then solve 'em.*/
sols=solve(start,fin) /*solve START ───► FINish. */
if sols <0 then exit 13 /*Was there a problem with input? */
if sols==0 then sols='No' /*Englishize the SOLS variable.*/
say; say sols 'unique solution's(sols) "found for" yyy
exit /*S [↑] does pluralizations. */
end
show=0 /*stop SOLVE from blabbing solutions.*/
do forever; rrrr=random(1111, 9999)
if pos(0, rrrr)\==0 then iterate /*if contains a zero, ignore it*/
if solve(rrrr)\==0 then leave /*if solved, then stop looking.*/
end /*forever*/
show=1 /*enable SOLVE to display solutions. */
rrrr=sort(rrrr) /*sort four digits (for consistancy). */
$.=0
do j=1 for length(rrrr) /*digit count for each digit in RRRR. */
_=substr(rrrr, j, 1) /*pick off one of the digits in RRRR. */
$._=countDigs(rrrr, _) /*define the count for this digit. */
end /*j*/ /* [↑] counts duplicates twice, no harm*/
prompt= 'Using the digits' rrrr", enter an expression that equals 24 (or QUIT):"
/* [↓] ITERATE needs a variable name.*/
do prompter=0; say; say prompt /*display blank line and the prompt (P)*/
pull y; y=space(y,0) /*get Y from CL, then remove all blanks*/
if abbrev('QUIT',y,1) then exit /*Does the user want to quit this game?*/
_v=verify(y, digs || opers || groupSymbols); a=substr(y, max(1,_v), 1)
if _v\==0 then do; call ger 'invalid character:' a; iterate; end
if pos('**',y) then do; call ger 'invalid ** operator'; iterate; end
if pos('//',y) then do; call ger 'invalid // operator'; iterate; end
yL=length(y)
if y=='' then do; call validate y; iterate; end
do j=1 for yL-1; if \datatype(substr(y, j ,1), 'W') then iterate
if \datatype(substr(y, j+1,1), 'W') then iterate
call ger 'invalid use of "digit abuttal".'
iterate prompter
end /*j*/
yd=countDigs(y, digs) /*count of the digits 1──►9 (123456789)*/
if yd<4 then do; call ger 'not enough digits entered.'; iterate prompter; end
if yd>4 then do; call ger 'too many digits entered.' ; iterate prompter; end
do j=1 for 9; if $.j==0 then iterate
_d=countDigs(y, j); if $.j==_d then iterate
if _d<$.j then call ger 'not enough' j "digits, must be" $.j
else call ger 'too many' j "digits, must be" $.j
iterate prompter
end /*j*/
y=translate(y, '()()', "[]{}")
interpret 'ans=' y; ans=ans/1; if ans==24 then leave prompter
say 'incorrect, ' y'='ans
end /*prompter*/
say; say center('', 79)
say center(' ', 79)
say center(' congratulations ! ', 79)
say center(' ', 79)
say center('', 79)
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────one─liner subroutines─────────────────────*/
countDigs: arg ?; return length(?) - length(space(translate(?, , arg(2)), 0))
div: if arg(1)=0 then return 7e9; return arg(1) /*if ÷ by 0, fudge result*/
ger: say; say '***error!*** for argument:' y; say arg(1); say;errCode=1;return 0
s: if arg(1)==1 then return ''; return 's' /*simple pluralizer.*/
syntax: call ger 'illegal syntax in' y; exit
/*──────────────────────────────────SOLVE subroutine──────────────────────────*/
solve: parse arg ssss, ffff /*parse the argument passed to SOLVE. */
if ffff=='' then ffff=ssss /*create a FFFF if necessary. */
if \validate(ssss) then return -1 /*validate the SSSS field. */
if \validate(ffff) then return -1 /* " " FFFF " */
#=0 /*number of found solutions (so far). */
!.=0 /*a method to hold unique expressions. */
/*alternative: indent=copies(' ',30) */
do g=ssss to ffff /*process a (possible) range of values.*/
if pos(0, g)\==0 then iterate /*ignore values with zero in them. */
do j=1 for 4 /*define a version for fast execution. */
g.j=substr(g, j, 1) /*extract each digit of G into array.*/
end /*j*/
do i =1 for ops /*insert an operator after 1st number. */
do j =1 for ops /*insert an operator after 2nd number. */
do k =1 for ops /*insert an operator after 2nd number. */
do m=0 to 3; L.= /*assume no left parenthesis so far. */
do n=m+1 to 4 /*match left paren with a right paren. */
L.m=Lpar /*define a left paren, m=0 means ignore*/
R.= /*un-define all right parenthesis. */
if m==1 & n==2 then L.= /*special case of : (n) + ··· */
else if m\==0 then R.n=Rpar /*no (, no )*/
e = L.1 g.1 @.i L.2 g.2 @.j L.3 g.3 R.3 @.k g.4 R.4
e=space(e, 0) /*remove all blanks from the expression*/
/* [↓] change expression: */
/* /(yyy) ═══► /div(yyy) */
/*Enables to check for division by zero*/
yyyE=e /*keep old the version for the display.*/
if pos('/(', e)\==0 then e=changestr( '/(', e, "/div(" )
/* [↓] INTERPRET stresses REXX's groin,*/
/* so try to avoid repeated lifting.*/
if !.e then iterate /*was this expression already used? */
!.e=1 /*mark this expression as being used. */
interpret 'x=' e /*have REXX do all the heavy lifting */
x=x/1 /*remove any trailing decimal point. */
if x\==24 then iterate /*Is the result incorrect? Try again. */
#=#+1 /*bump number of found solutions. */
_=translate(yyyE, '][', ")(") /*display [], not (). */
if show then say indent 'a solution:' _
end /*n*/ /* [↑] show a solution.*/
end /*m*/
end /*k*/
end /*j*/
end /*i*/
end /*g*/
return #
/*──────────────────────────────────SORT subroutine───────────────────────────*/
sort: procedure; arg nnnn; L=length(nnnn)
do i=1 for L /*build an array of digits from NNNN. */
s.i=substr(nnnn, i, 1) /*this enables SORT to sort an array. */
end /*i*/
do j=1 for L; _=s.j
do k=j+1 to L
if s.k<_ then parse value s.j s.k with s.k s.j
end /*k*/
end /*j*/
return s.1 || s.2 || s.3 || s.4
/*──────────────────────────────────VALIDATE subroutine───────────────────────*/
validate: parse arg y; errCode=0; _v=verify(y,digs)
select
when y=='' then call ger 'no digits entered.'
when length(y)<4 then call ger 'not enough digits entered, must be four.'
when length(y)>4 then call ger 'too many digits entered, must be four.'
when pos(0,y)\==0 then call ger "can't use the digit 0 (zero)."
when _v\==0 then call ger 'illegal character: ' substr(y,_v,1)
otherwise nop
end /*select*/
return \errCode

View file

@ -0,0 +1,25 @@
changestr: Procedure
/* change needle to newneedle in haystack (as often as specified */
/* or all of them if count is omitted */
Parse Arg needle,haystack,newneedle,count
If count>'' Then Do
If count=0 Then Do
Say 'chstr count must be > 0'
Signal Syntax
End
End
res=""
changes=0
px=1
do Until py=0
py=pos(needle,haystack,px)
if py>0 then Do
res=res||substr(haystack,px,py-px)||newneedle
px=py+length(needle)
changes=changes+1
If count>'' Then
If changes=count Then Leave
End
end
res=res||substr(haystack,px)
Return res

View file

@ -1,4 +1,4 @@
require "rational"
require "rational" # for Ruby versions before 2.0
def play
digits = Array.new(4){rand(1..9)}

View file

@ -0,0 +1,30 @@
gen_digits() {
awk 'BEGIN { srand()
for(i = 1; i <= 4; i++) print 1 + int(9 * rand())
}' | sort
}
same_digits() {
[ "$(tr -dc 0-9 | sed 's/./&\n/g' | grep . | sort)" = "$*" ]
}
guessed() {
[ "$(echo "$1" | tr -dc '\n0-9()*/+-' | bc 2>/dev/null)" = 24 ]
}
while :
do
digits=$(gen_digits)
echo
echo Digits: $digits
read -r expr
echo " $expr" | same_digits "$digits" || \
{ echo digits should be: $digits; continue; }
guessed "$expr" && message=correct \
|| message=wrong
echo $message
done