FootballHero.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Doug Terry, December 30, 1992 11:38 am PST
A computer controlled football player.
DIRECTORY
Commander USING [CommandProc, Handle, Register],
CommanderOps USING [NextArgument],
FS USING [StreamOpen, Error],
IO USING [Close, EndOf, EndOfStream, GetInt, GetLineRope, GetTokenRope, IDProc, rope, RIS, STREAM],
Process USING [Detach, Pause, MsecToTicks, SecondsToTicks],
Random USING [RandomStream, Create, ChooseInt],
Rope USING [Equal, ROPE],
SimpleFeedback,
FootballInternal,
FootballMaster;
FootballHero: CEDAR PROGRAM
IMPORTS Commander, CommanderOps, FootballInternal, FootballMaster, FS, IO, Rope, Process, Random, SimpleFeedback
= { OPEN FootballMaster, FootballInternal;
Global data
QBStates: TYPE = {CallingPlay, CallingSignals, WaitingForSnap, BackToPass, BackToKick, ExecutingPlay};
delay: CARDINAL ← 25;
rs: Random.RandomStream;
teamInfo: ARRAY Team OF T ← ALL[NIL];
defaultPlaybookFile: Rope.ROPE ← "./FootballHero.plays";
Side: TYPE = {offense, defense}; -- from FootballMaster
PlayType: TYPE = {setup, play};
Playbook: TYPE = ARRAY Side OF ARRAY PlayType OF RECORD[
num: INT ← 0,
plays: ARRAY [0..100] OF Play
];
PuntingPlaybook: TYPE = ARRAY Side OF ARRAY PlayType OF Play;
puntingPlaysInit: BOOLEANFALSE;
puntingPlays: PuntingPlaybook;
teamPlays: ARRAY Team OF Playbook;
teamPlaybookFile: ARRAY Team OF ROPE;
Main control loop
ReportState: PROC [t: T, msg: ROPE] RETURNS [] ~ {
state: ROPESELECT t.game.state FROM
offField => "offField",
huddle => "huddle",
setUp => "setUp",
option => "option",
pass => "pass",
catchable => "catchable",
liveBall => "liveBall",
run => "run",
turnover => "turnover",
ENDCASE => "other";
SimpleFeedback.PutF[$FBHero, $begin, NIL, "team=%s", IO.rope[IF t.myTeam = home THEN "home" ELSE "visitors"]];
SimpleFeedback.PutF[$FBHero, $middle, NIL, ",state=%s: ", IO.rope[state]];
SimpleFeedback.Append[$FBHero, $end, NIL, msg];
};
AutoControl: PROC [myTeam: Team, playbookFile: ROPE] RETURNS [] ~ TRUSTED {
t: T ← teamInfo[myTeam];
game: Game ← t.game;
qbstate: QBStates ← CallingPlay;
myQB: Position ← IF myTeam = home THEN HQB ELSE VQB;
formation: Play;
play: Play;
SetUpPlaybook[myTeam, playbookFile];
DO
Process.Pause[Process.MsecToTicks[delay]];
IF teamInfo[myTeam] = NIL THEN EXIT;
SELECT qbstate FROM
CallingPlay => {
ReportState[t, "Calling play..."];
[formation, play] ← CallPlay[myTeam, game];
Process.Pause[Process.SecondsToTicks[5]]; -- time in huddle
set formation
ReportState[t, "Setting formation..."];
FOR i: CARDINAL IN [0..6) DO
FootballInternal.SetCommand[t: t, player: Player[myTeam, i], command: formation.commands[i], playSetUp: TRUE];
FootballInternal.SetCommandUp[t: t, player: Player[myTeam, i], command: formation.commands[i], playSetUp: TRUE];
ENDLOOP;
FootballMaster.SetUp[myTeam];
ReportState[t, "Lining up..."];
Process.Pause[Process.SecondsToTicks[5]]; -- time to get to line of scrimage
set play
ReportState[t, "Setting play..."];
FOR i: CARDINAL IN [0..6) DO
FootballInternal.SetCommand[t: t, player: Player[myTeam, i], command: play.commands[i], playSetUp: FALSE];
ENDLOOP;
ReportState[t, "Calling signals or waiting for snap..."];
qbstate ← IF game.offense = myTeam THEN CallingSignals ELSE WaitingForSnap;
Process.Pause[Process.SecondsToTicks[2]]; -- time to call signals
};
CallingSignals => {
FootballMaster.Hike[myTeam];
Process.Pause[5]; -- wait long enough to see if ball was actually snapped
IF game.state > setUp THEN
qbstate ← SELECT play.ball.action FROM
pass => BackToPass,
kick => BackToKick,
ENDCASE => ExecutingPlay;
};
WaitingForSnap => {
IF game.state > setUp THEN
qbstate ← ExecutingPlay;
};
BackToPass => {
IF game.ballCarrier = myQB AND ReceiverInArea[myTeam, game, play.ball.target, 5] THEN {
Process.Pause[20]; -- give receiver a few more seconds to get to spot
FootballInternal.SetCommand[t, ball, play.ball];
qbstate ← ExecutingPlay;
};
IF game.state < option THEN -- play must be over
qbstate ← CallingPlay;
};
BackToKick => {
IF game.ballCarrier = myQB THEN {
FootballInternal.SetCommand[t, ball, [kick, [goal[]]]];
qbstate ← ExecutingPlay;
};
IF game.state < option THEN -- play must be over
qbstate ← CallingPlay;
};
ExecutingPlay => {
IF game.state < option THEN -- play must be over
qbstate ← CallingPlay;
};
ENDCASE;
IF game.penaltyState = asking AND game.penalties[Opponent[myTeam]] # none THEN {
FootballMaster.PenaltyResponse[myTeam, TRUE];
qbstate ← CallingPlay;
};
ENDLOOP;
};
Play calling
SetUpPuntingPlaybook: PROC [] RETURNS [] ~ {
IF puntingPlaysInit = TRUE THEN RETURN;
puntingPlays[offense][setup] ← NEW[PlayRec];
puntingPlays[defense][setup] ← NEW[PlayRec];
puntingPlays[offense][play] ← NEW[PlayRec];
puntingPlays[defense][play] ← NEW[PlayRec];
puntingPlays[offense][setup]^ ← [name: "Punt-formation", commands: [
[run, [relative[-3, 0]]], [run, [relative[-1, -8]]], [run, [relative[-1, 8]]],
[run, [relative[-1, -4]]], [run, [relative[-1, 4]]], [run, [relative[-1, 0]]]]];
puntingPlays[defense][setup]^ ← [name: "Punt-reception", commands: [
[run, [relative[-45, 0]]], [run, [relative[-1, -8]]], [run, [relative[-1, 8]]],
[run, [relative[-1, -4]]], [run, [relative[-1, 4]]], [run, [relative[-1, 0]]]]];
puntingPlays[offense][play]^ ← [name: "Punt", ball: [kick, [goal[]]], commands: [
[run, [relative[-10, 3]]], [block, [player[all]]], [block, [player[all]]],
[block, [player[all]]], [block, [player[all]]], [block, [player[all]]]]];
puntingPlays[defense][play]^ ← [name: "Punt-return", commands: [
[stop, [null[]]], [block, [player[ball]]], [block, [player[ball]]],
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]];
puntingPlaysInit ← TRUE;
};
ManualSetUpPlaybook: PROC [myTeam: Team] RETURNS [] ~ {
this should really read a playbook file
SetUpPuntingPlaybook[];
teamPlaybookFile[myTeam] ← "manual";
teamPlays[myTeam][offense][setup].plays[0] ← NEW[PlayRec];
teamPlays[myTeam][defense][setup].plays[0] ← NEW[PlayRec];
teamPlays[myTeam][offense][play].plays[0] ← NEW[PlayRec];
teamPlays[myTeam][offense][play].plays[1] ← NEW[PlayRec];
teamPlays[myTeam][defense][play].plays[0] ← NEW[PlayRec];
teamPlays[myTeam][defense][play].plays[1] ← NEW[PlayRec];
teamPlays[myTeam][defense][play].plays[2] ← NEW[PlayRec];
teamPlays[myTeam][defense][play].plays[3] ← NEW[PlayRec];
offensive setups
teamPlays[myTeam][offense][setup].plays[0]^ ← [name: "5-1", commands: [
[run, [relative[-3, 0]]], [run, [relative[-1, -8]]], [run, [relative[-1, 8]]],
[run, [relative[-1, -4]]], [run, [relative[-1, 4]]], [run, [relative[-1, 0]]]]];
teamPlays[myTeam][offense][setup].num ← 1;
defensive setups
teamPlays[myTeam][defense][setup].plays[0]^ ← [name: "4-2", commands: [
[run, [relative[-1, -2]]], [run, [relative[-6, -2]]], [run, [relative[-6, 2]]],
[run, [relative[-1, -6]]], [run, [relative[-1, 6]]], [run, [relative[-1, 2]]]]];
teamPlays[myTeam][defense][setup].num ← 1;
offensive plays
teamPlays[myTeam][offense][play].plays[0]^ ← [name: "UpTheMiddle", commands: [
[run, [relative[5, -4]]], [block, [player[all]]], [block, [player[all]]],
[block, [player[all]]], [block, [player[all]]], [block, [player[all]]]]];
teamPlays[myTeam][offense][play].plays[1]^ ← [name: "ShortPassTop", ball: [pass, [relative[10, 10]]], commands: [
[run, [relative[-10, 3]]], [block, [player[all]]], [run, [relative[10, 10]]],
[block, [player[all]]], [block, [player[all]]], [block, [player[all]]]]];
teamPlays[myTeam][offense][play].num ← 2;
defensive plays
teamPlays[myTeam][defense][play].plays[0]^ ← [name: "Rush", commands: [
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]],
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]];
teamPlays[myTeam][defense][play].plays[1]^ ← [name: "GuardEnds", commands: IF myTeam = home THEN [
[run, [player[ball]]], [run, [player[VLE]]], [run, [player[VRE]]],
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]
ELSE [
[run, [player[ball]]], [run, [player[HLE]]], [run, [player[HRE]]],
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]];
teamPlays[myTeam][defense][play].plays[2]^ ← [name: "ManToMan", commands: IF myTeam = home THEN [
[run, [player[VQB]]], [run, [player[VLE]]], [run, [player[VRE]]],
[run, [player[VLG]]], [run, [player[VRG]]], [run, [player[VC]]]]
ELSE [
[run, [player[HQB]]], [run, [player[HLE]]], [run, [player[HRE]]],
[run, [player[HLG]]], [run, [player[HRG]]], [run, [player[HC]]]]];
teamPlays[myTeam][defense][play].plays[3]^ ← [name: "Zone", commands: [
[run, [player[ball]]], [block, [relative[-10, -15]]], [block, [relative[-10, 15]]],
[run, [player[ball]]], [run, [player[ball]]], [run, [player[ball]]]]];
teamPlays[myTeam][defense][play].num ← 4;
};
SetUpPlaybook: PROC [myTeam: Team, playbookFile: ROPE] RETURNS [] ~ {
-- stolen from FootballPlays.mesa
ENABLE FS.Error => {ManualSetUpPlaybook[myTeam]; CONTINUE};
play: Play;
line, token: Rope.ROPE;
stream, file: IO.STREAM;
IF playbookFile = NIL THEN
playbookFile ← defaultPlaybookFile;
teamPlaybookFile[myTeam] ← playbookFile;
SetUpPuntingPlaybook[];
file ← FS.StreamOpen[playbookFile];
WHILE ~file.EndOf[] DO
ENABLE IO.EndOfStream => LOOP;
line ← file.GetLineRope[]; -- reads but doesn't return CR
stream ← IO.RIS[line, stream];
token ← stream.GetTokenRope[IO.IDProc].token;
SELECT TRUE FROM
Rope.Equal[token, "--"] => LOOP;
Rope.Equal[token, "QB"] => play.commands[0] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "LE"] => play.commands[1] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "RE"] => play.commands[2] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "LG"] => play.commands[3] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "RG"] => play.commands[4] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "C"] => play.commands[5] ← ReadCommand[myTeam, stream];
Rope.Equal[token, "ball"] => play.ball ← ReadCommand[myTeam, stream, TRUE];
ENDCASE => {
IF play # NIL AND play.name # NIL THEN AddPlay[myTeam, play];
play ← NEW[PlayRec ← []];
play.name ← token;
token ← stream.GetTokenRope[IO.IDProc].token;
IF Rope.Equal[token, "defensive"] THEN play.side ← defense;
token ← stream.GetTokenRope[IO.IDProc].token;
IF Rope.Equal[token, "play"] THEN play.setUp ← FALSE};
ENDLOOP;
IF play # NIL AND play.name # NIL THEN AddPlay[myTeam, play];
file.Close[];
};
ReadCommand: PROC[myTeam: Team, stream: IO.STREAM, ball: BOOLEANFALSE] RETURNS[Command] = TRUSTED {
-- stolen from FootballPlays.mesa
token: Rope.ROPE;
action: Action ← block;
target: Target ← [player[ball]];
WHILE ~stream.EndOf[] DO
token ← stream.GetTokenRope[IO.IDProc].token;
SELECT TRUE FROM
Rope.Equal[token, "stop"] => RETURN[[stop, [null[ ]]]];
Rope.Equal[token, "block"] => action ← block;
Rope.Equal[token, "guard"] => action ← block;
Rope.Equal[token, "run"] => action ← run;
Rope.Equal[token, "pass"] => IF ball THEN action ← pass;
Rope.Equal[token, "kick"] => IF ball THEN action ← kick;
Rope.Equal[token, "goal"] => target ← [goal[]];
Rope.Equal[token, "kick"] => IF ball THEN action ← kick;
Rope.Equal[token, "ball"] => target ← [player[ball]];
Rope.Equal[token, "QB"] => target ← [player[IF myTeam = home THEN VQB ELSE HQB]];
Rope.Equal[token, "LE"] => target ← [player[IF myTeam = home THEN VLE ELSE HLE]];
Rope.Equal[token, "RE"] => target ← [player[IF myTeam = home THEN VRE ELSE HRE]];
Rope.Equal[token, "LG"] => target ← [player[IF myTeam = home THEN VLG ELSE HLG]];
Rope.Equal[token, "RG"] => target ← [player[IF myTeam = home THEN VRG ELSE HRG]];
Rope.Equal[token, "C"] => target ← [player[IF myTeam = home THEN VC ELSE HC]];
Rope.Equal[token, "location"] => {x, y: INTEGER;
x ← stream.GetInt[];
y ← stream.GetInt[];
target ← [relative[x, y]]};
ENDCASE;
ENDLOOP;
RETURN[[action, target]];
};
AddPlay: PROC[myTeam: Team, new: Play] = {
pt: PlayType = IF new.setUp THEN setup ELSE play;
teamPlays[myTeam][new.side][pt].plays[teamPlays[myTeam][new.side][pt].num] ← new;
teamPlays[myTeam][new.side][pt].num ← teamPlays[myTeam][new.side][pt].num + 1;
};
CallPlay: PROC [myTeam: Team, game: Game] RETURNS [formation, play: Play] ~ {
myside: Side = IF game.offense = myTeam THEN offense ELSE defense;
IF game.down = 4 THEN {
formation ← puntingPlays[myside][setup];
play ← puntingPlays[myside][play];
}
ELSE {
formation ← teamPlays[myTeam][myside][setup].plays[Random.ChooseInt[rs: rs, max: teamPlays[myTeam][myside][setup].num-1]];
play ← teamPlays[myTeam][myside][play].plays[Random.ChooseInt[rs: rs, max: teamPlays[myTeam][myside][play].num-1]];
};
};
ReceiverInArea: PROC [myTeam: Team, game: Game, area: Target, delta: REAL ← 1] RETURNS [BOOL] ~ {
x, y: REAL;
[x, y] ← GetTarget[myTeam, game, area];
FOR i: CARDINAL IN [0..6) DO
IF ABS[y - game.player[Player[myTeam, i]].y] < delta AND ABS[x - game.player[Player[myTeam, i]].x] < delta THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
GetTarget: PROC [myTeam: Team, game: Game, target: Target] RETURNS [tx, ty: REAL] ~ {
goal: INTEGER;
TRUSTED {
goal ← Goal[Opponent[myTeam], game.quarter, TRUE];
};
WITH t: target SELECT FROM
goal => RETURN [goal, fieldWidth/2];
player => RETURN[game.player[t.position].x, game.player[t.position].y];
relative => RETURN [game.scrimmage + (IF goal > 50 THEN t.x ELSE -t.x),
fieldWidth/2 + t.y];
absolute => RETURN [t.x, t.y];
ENDCASE => ERROR;
};
Player: PROC [myTeam: Team, i: INTEGER] RETURNS [Position] ~ TRUSTED INLINE {
myQB: Position ← IF myTeam = home THEN HQB ELSE VQB;
RETURN[LOOPHOLE[LOOPHOLE[myQB, INTEGER]+i]];
};
Registrations and command procs
StartStop: Commander.CommandProc = {
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
file: Rope.ROPE;
myTeam: Team;
IF CommanderOps.NextArgument[cmd].Equal["visitors", FALSE]
THEN myTeam ← visitors
ELSE myTeam ← home;
IF CommanderOps.NextArgument[cmd].Equal["stop", FALSE] THEN {
IF teamInfo[myTeam] = NIL THEN RETURN[msg: "Football hero not running"];
teamInfo[myTeam] ← NIL;
RETURN[msg: "Football hero stopped"];
};
IF teamInfo[myTeam] # NIL THEN RETURN[msg: "Football hero already running"];
file ← CommanderOps.NextArgument[cmd];
IF rs = NIL THEN
rs ← Random.Create[seed: -1];
TRUSTED {
teamInfo[myTeam] ← FootballInternal.GetTFromTeam[myTeam];
IF teamInfo[myTeam] = NIL THEN RETURN[msg: "Football team not currently on the field"];
Process.Detach[FORK AutoControl[myTeam, file]];
};
};
Commander.Register[key: "FootballHero", proc: StartStop, doc: "Computerized control for football arcade game.\n usage: FootballHero {home|visitors} {start|stop} [playbook]"];
} . . .