280 lines
5.4 KiB
ObjectPascal
280 lines
5.4 KiB
ObjectPascal
var ErrorFlag: boolean;
|
|
var ErrorStr: string;
|
|
|
|
|
|
function EvaluateExpression(Express: string): double;
|
|
{ Recursive descent expression evaluator }
|
|
var Atom: char;
|
|
var ExpressStr: string;
|
|
var ExpressInx: integer;
|
|
const Tab_Char = #$09; SP_char = #$20;
|
|
|
|
procedure HandleError(S: string);
|
|
begin
|
|
ErrorStr:=S;
|
|
ErrorFlag:=True;
|
|
Abort;
|
|
end;
|
|
|
|
|
|
procedure GetChar;
|
|
begin
|
|
if ExpressInx > Length(ExpressStr) then
|
|
begin
|
|
Atom:= ')';
|
|
end
|
|
else begin
|
|
Atom:= ExpressStr[ExpressInx];
|
|
Inc(ExpressInx);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
procedure SkipWhiteSpace;
|
|
{ Skip Tabs And Spaces In Expression }
|
|
begin
|
|
while (Atom=TAB_Char) or (Atom=SP_char) do GetChar;
|
|
end;
|
|
|
|
|
|
|
|
procedure SkipSpaces;
|
|
{ Get Next Character, Ignoring Any Space Characters }
|
|
begin
|
|
repeat GetChar until Atom <> SP_CHAR;
|
|
end;
|
|
|
|
|
|
|
|
function GetDecimal: integer;
|
|
{ Read In A Decimal String And Return Its Value }
|
|
var S: string;
|
|
begin
|
|
Result:=0;
|
|
S:='';
|
|
while True do
|
|
begin
|
|
if not (Atom in ['0'..'9']) then break;
|
|
S:=S+Atom;
|
|
GetChar;
|
|
end;
|
|
if S='' then HandleError('Number Expected')
|
|
else Result:=StrToInt(S);
|
|
if Result>9 then HandleError('Only Numbers 0..9 allowed')
|
|
end;
|
|
|
|
|
|
function Expression: double;
|
|
{ Returns The Value Of An Expression }
|
|
|
|
|
|
|
|
function Factor: double;
|
|
{ Returns The Value Of A Factor }
|
|
var NEG: boolean;
|
|
begin
|
|
Result:=0;
|
|
while Atom='+' do SkipSpaces; { Ignore Unary "+" }
|
|
NEG:= False;
|
|
while Atom ='-' do { Unary "-" }
|
|
begin
|
|
SkipSpaces;
|
|
NEG:= not NEG;
|
|
end;
|
|
|
|
if (Atom>='0') and (Atom<='9') then Result:= GetDecimal { Unsigned Integer }
|
|
else case Atom of
|
|
'(': begin { Subexpression }
|
|
SkipSpaces;
|
|
Result:= Expression;
|
|
if Atom<>')' then HandleError('Mismatched Parenthesis');
|
|
SkipSpaces;
|
|
end;
|
|
else HandleError('Syntax Error');
|
|
end;
|
|
{ Numbers May Terminate With A Space Or Tab }
|
|
SkipWhiteSpace;
|
|
if NEG then Result:=-Result;
|
|
end; { Factor }
|
|
|
|
|
|
|
|
function Term: double;
|
|
{ Returns Factor * Factor, Etc. }
|
|
var R: double;
|
|
begin
|
|
Result:= Factor;
|
|
while True do
|
|
case Atom of
|
|
'*': begin
|
|
SkipSpaces;
|
|
Result:= Result * Factor;
|
|
end;
|
|
'/': begin
|
|
SkipSpaces;
|
|
R:=Factor;
|
|
if R=0 then HandleError('Divide By Zero');
|
|
Result:= Result / R;
|
|
end;
|
|
else break;
|
|
end;
|
|
end;
|
|
{ Term }
|
|
|
|
|
|
|
|
function AlgebraicExpression: double;
|
|
{ Returns Term + Term, Etc. }
|
|
begin
|
|
Result:= Term;
|
|
while True do
|
|
case Atom of
|
|
'+': begin SkipSpaces; Result:= Result + Term; end;
|
|
'-': begin SkipSpaces; Result:= Result - Term; end
|
|
else break;
|
|
end;
|
|
end; { Algexp }
|
|
|
|
|
|
|
|
begin { Expression }
|
|
SkipWhiteSpace;
|
|
Result:= AlgebraicExpression;
|
|
end; { Expression }
|
|
|
|
|
|
|
|
begin { EvaluateExpression }
|
|
ErrorFlag:=False;
|
|
ErrorStr:='';
|
|
ExpressStr:=Express;
|
|
ExpressInx:=1;
|
|
try
|
|
GetChar;
|
|
Result:= Expression;
|
|
except end;
|
|
end;
|
|
|
|
|
|
function WaitForString(Memo: TMemo; Prompt: string): string;
|
|
{Wait for key stroke on TMemo component}
|
|
var MW: TMemoWaiter;
|
|
var C: char;
|
|
var Y: integer;
|
|
begin
|
|
{Use custom object to wait and capture key strokes}
|
|
MW:=TMemoWaiter.Create(Memo);
|
|
try
|
|
Memo.Lines.Add(Prompt);
|
|
Memo.SelStart:=Memo.SelStart-1;
|
|
Memo.SetFocus;
|
|
Result:=MW.WaitForLine;
|
|
finally MW.Free; end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure Play24Game(Memo: TMemo);
|
|
{Play the 24 game}
|
|
var R: double;
|
|
var Nums: array [0..4-1] of char;
|
|
var I: integer;
|
|
var Express,RS: string;
|
|
var RB: boolean;
|
|
|
|
procedure GenerateNumbers;
|
|
{Generate and display four random number 1..9}
|
|
var S: string;
|
|
var I: integer;
|
|
begin
|
|
{Generate random numbers}
|
|
for I:=0 to High(Nums) do
|
|
Nums[I]:=char(Random(9)+$31);
|
|
{Display them}
|
|
S:='';
|
|
for I:=0 to High(Nums) do
|
|
S:=S+' '+Nums[I];
|
|
Memo.Lines.Add('Your Digits: '+S);
|
|
end;
|
|
|
|
function TestMatchingNums: boolean;
|
|
{Make sure numbers entered by user match the target numbers}
|
|
var SL1,SL2: TStringList;
|
|
var I: integer;
|
|
begin
|
|
Result:=False;
|
|
SL1:=TStringList.Create;
|
|
SL2:=TStringList.Create;
|
|
try
|
|
{Load target numbers into string list}
|
|
for I:=0 to High(Nums) do SL1.Add(Nums[I]);
|
|
{Load users expression number int string list}
|
|
for I:=1 to Length(Express) do
|
|
if Express[I] in ['0'..'9'] then SL2.Add(Express[I]);
|
|
{There should be the same number }
|
|
if SL1.Count<>SL2.Count then exit;
|
|
{Sort them to facilitate testing}
|
|
SL1.Sort; SL2.Sort;
|
|
{Are number identical, if not exit}
|
|
for I:=0 to SL1.Count-1 do
|
|
if SL1[I]<>SL2[I] then exit;
|
|
{Users numbers passed all tests}
|
|
Result:=True;
|
|
finally
|
|
SL2.Free;
|
|
SL1.Free;
|
|
end;
|
|
end;
|
|
|
|
function TestUserExpression(var S: string): boolean;
|
|
{Test expression user entered }
|
|
begin
|
|
Result:=False;
|
|
if not TestMatchingNums then
|
|
begin
|
|
S:='Numbers Do not Match';
|
|
exit;
|
|
end;
|
|
|
|
R:=EvaluateExpression(Express);
|
|
S:='Expression Value = '+FloatToStrF(R,ffFixed,18,0)+CRLF;
|
|
if ErrorFlag then
|
|
begin
|
|
S:=S+'Expression Problem: '+ErrorStr;
|
|
exit;
|
|
end;
|
|
if R<>24 then
|
|
begin
|
|
S:=S+'Expression is incorrect value';
|
|
exit;
|
|
end;
|
|
S:=S+'!!!!!! Winner !!!!!!!';
|
|
Result:=True;
|
|
end;
|
|
|
|
|
|
begin
|
|
Randomize;
|
|
Memo.Lines.Add('=========== 24 Game ===========');
|
|
GenerateNumbers;
|
|
while true do
|
|
begin
|
|
if Application.Terminated then exit;
|
|
Express:=WaitForString(Memo,'Enter expression, Q = quit, N = New numbers: '+CRLF);
|
|
if Pos('N',UpperCase(Express))>0 then
|
|
begin
|
|
GenerateNumbers;
|
|
Continue;
|
|
end;
|
|
if Pos('Q',UpperCase(Express))>0 then exit;
|
|
RB:=TestUserExpression(RS);
|
|
Memo.Lines.Add(RS);
|
|
if not RB then continue;
|
|
RS:=WaitForString(Memo,'Play again Y=Yes, N=No'+CRLF);
|
|
if Pos('N',UpperCase(RS))>0 then exit;
|
|
GenerateNumbers;
|
|
end;
|
|
end;
|