DIRECTORY FootballInternal, FootballMaster, FootballMasterRpcControl USING [ExportInterface, UnexportInterface], Process USING [SetTimeout], RealFns USING [SqRt], Rope USING [ROPE], RPC USING [MakeKey], System USING [GetGreenwichMeanTime, GreenwichMeanTime, Pulses, GetClockPulses], UserExec USING [GetNameAndPassword]; FootballControl: MONITOR IMPORTS FootballMaster, FootballMasterRpcControl, Process, RealFns, RPC, System, UserExec EXPORTS FootballInternal, FootballMaster = BEGIN OPEN FootballMaster; process: PROCESS _ NIL; StartServer: PUBLIC ENTRY PROCEDURE[name: Rope.ROPE] = BEGIN user, password: Rope.ROPE; IF stopped = FALSE THEN RETURN ELSE stopped _ FALSE; process _ FORK Main[]; [user, password] _ UserExec.GetNameAndPassword[]; FootballMasterRpcControl.ExportInterface[ interfaceName: [instance: name], user: user, password: RPC.MakeKey[password]]; END; StopServer: PUBLIC ENTRY PROCEDURE = BEGIN IF stopped = TRUE THEN RETURN ELSE stopped _ TRUE; WHILE process # NIL DO WAIT timer; ENDLOOP; FootballMasterRpcControl.UnexportInterface[]; END; Init: PROC = BEGIN game _ NEW[GameRec _ []]; FOR i: Position IN Players DO game.player[i].position _ i; ENDLOOP; END; game: Game; slow: BOOLEAN _ FALSE; commands: ARRAY Team OF Commands _ [ALL[[stop, [null[]]]], ALL[[stop, [null[]]]]]; SetCommands: PUBLIC PROC[team: Team, c: Commands] RETURNS[GameRec] = BEGIN commands[team] _ c; RETURN[game^]; END; OffField: PROCEDURE = BEGIN game.state _ offField; game.ballCarrier _ none; game.player[ball].x _ -100; game.player[ball].y _ -100; game.ballTarget _ [absolute[-100, -100]]; game.clockStopped _ TRUE; END; KickOff: PUBLIC PROCEDURE[team: Team] = BEGIN IF game.quarter = 5 THEN { game.quarter _ 1; game.score _ [0, 0]}; IF game.quarter = 1 AND game.clock = 15*60 THEN team _ home; IF game.quarter = 3 AND game.clock = 15*60 THEN team _ visitors; game.offense _ game.possessor _ team; IF Goal[team, game.quarter] < 50 THEN game.scrimmage _ 20 ELSE game.scrimmage _ 80; FirstDown[]; Huddle[]; END; Huddle: PROCEDURE = BEGIN FOR i: Position IN Players DO game.player[i].dx _ 0; game.player[i].dy _ 0; ENDLOOP; game.player[ball].x _ game.scrimmage; game.player[ball].y _ fieldWidth/2; game.ballCarrier _ none; game.ballTarget _ [absolute[game.scrimmage, fieldWidth/2]]; game.state _ huddle; game.pass _ FALSE; game.lateral _ FALSE; delay _ 1; startHuddle _ game.clock; END; SetUp: PUBLIC PROCEDURE[team: Team] = BEGIN IF game.state = offField THEN KickOff[team]; game.state _ setUp; END; Hike: PUBLIC PROCEDURE[team: Team] = BEGIN hike: REAL; count: NAT _ 0; IF team # game.offense THEN RETURN; IF ~Ready[home] OR ~Ready[visitors] THEN RETURN; delay _ IF slow THEN 5 ELSE 1; game.penalties _ [none, none]; game.penaltyState _ none; game.clockStopped _ FALSE; hike _ game.scrimmage - Direction[game.offense, game.quarter]*15; game.player[ball].x _ game.scrimmage - Direction[game.offense, game.quarter]*1.1; game.ballTarget _ [absolute[hike, fieldWidth/2]]; game.ballCarrier _ ball; FOR i: Position IN Players DO game.player[i].blocking _ none; -- reset IF TeamOf[i] = game.offense AND InMotion[@game.player[i]] THEN IF count = 0 THEN count _ count + 1 ELSE game.penalties[game.offense] _ formation; IF CrossLine[game.player[i].x, game.scrimmage, TeamOf[i], game.quarter] THEN game.penalties[TeamOf[i]] _ offSides; ENDLOOP; game.state _ option; END; InMotion: PROC[player: Player] RETURNS[BOOLEAN] = INLINE {RETURN[ABS[player.dx] > .5 OR ABS[player.dy] > .5]}; Ready: PROC[team: Team] RETURNS[BOOLEAN] = INLINE BEGIN count: NAT _ 0; FOR i: Position IN Players DO IF TeamOf[i] # team THEN LOOP; IF ABS[game.player[i].x - game.scrimmage] < 2 THEN count _ count + 1; ENDLOOP; RETURN[count > 2]; END; FirstDown: PROC = BEGIN game.down _ 1; IF Goal[game.offense, game.quarter] < 50 THEN game.firstDownMarker _ MIN[100, game.scrimmage + 10] ELSE game.firstDownMarker _ MAX[0, game.scrimmage - 10]; END; TimeOut: PUBLIC PROCEDURE[team: Team] = BEGIN IF game.timeouts[team] = 0 THEN RETURN; IF game.state > option THEN RETURN; IF game.clockStopped THEN RETURN; game.clockStopped _ TRUE; game.timeouts[team] _ game.timeouts[team] - 1; END; PenaltyResponse: PUBLIC PROCEDURE[team: Team, accepted: BOOLEAN] = BEGIN IF game.penalties[Opponent[team]] = none THEN RETURN; IF game.penaltyState # asking THEN RETURN; game.penaltyState _ IF accepted THEN accepted ELSE declined; END; DisableDelayOfGame: PUBLIC PROCEDURE = {delayOfGame _ FALSE}; timer: CONDITION; delay: CARDINAL _ 1; stopped: BOOLEAN _ TRUE; gmt: System.GreenwichMeanTime _ [0]; startHuddle: INTEGER _ 0; delayOfGame: BOOLEAN _ TRUE; Wait: ENTRY PROCEDURE[i: CARDINAL] = BEGIN DO IF i = 0 THEN EXIT ELSE i _ i - 1; WAIT timer; ENDLOOP; END; Main: PROCEDURE = BEGIN x, y: REAL; player: Player; command: Commands; Process.SetTimeout[@timer, 1]; SetOffField[]; DO Wait[delay]; IF stopped THEN {process _ NIL; EXIT}; IF ~game.clockStopped AND game.quarter # 5 THEN IF System.GetGreenwichMeanTime[] # gmt THEN { game.clock _ game.clock - 1; gmt _ System.GetGreenwichMeanTime[]}; IF game.clock <= 0 AND game.state < option THEN PlayOver[game.scrimmage]; IF delayOfGame AND game.state < option AND startHuddle - game.clock > 60 THEN { SELECT TRUE FROM ~Ready[home] AND ~Ready[visitors] => {startHuddle _ game.clock; LOOP}; ~Ready[home] => game.penalties[home] _ delayOfGame; ~Ready[visitors] => game.penalties[visitors] _ delayOfGame; ENDCASE => game.penalties[game.offense] _ delayOfGame; PlayOver[game.scrimmage, TRUE]}; IF (game.penalties[home] = offSides OR game.penalties[visitors] = offSides) AND game.state > setUp THEN PlayOver[game.scrimmage, TRUE]; x _ game.player[ball].x; y _ game.player[ball].y; IF game.state > setUp AND (x NOT IN [-10..110] OR y NOT IN [0..fieldWidth]) THEN PlayOver[x, TRUE]; IF game.state > setUp AND game.ballCarrier # ball AND Direction[TeamOf[game.ballCarrier], game.quarter] = 1 AND x > 100 THEN PlayOver[101, TRUE]; IF game.state > setUp AND game.ballCarrier # ball AND Direction[TeamOf[game.ballCarrier], game.quarter] = -1 AND x < 0 THEN PlayOver[-1, TRUE]; IF game.state > setUp AND game.ballCarrier = none THEN { [x, y] _ GetTarget[game.ballTarget, none]; MoveBall[@game.player[ball], x, y]; CatchBall[]; IF game.player[ball].x = x AND game.player[ball].y = y THEN -- ball has hit the ground IF game.pass AND ~game.lateral THEN PlayOver[game.scrimmage, TRUE] ELSE game.state _ liveBall}; IF game.state = option AND CrossLine[x, game.scrimmage, game.offense, game.quarter] THEN game.state _ run; command _ commands[home]; FOR i: CARDINAL IN [0..6) DO player _ @game.player[Add[HQB, i]]; IF game.state IN [offField..setUp] THEN { IF command[i].action # run THEN LOOP; [x, y] _ GetTarget[command[i].target, player.position]; Move[player, x, y, TRUE]; LOOP}; PlayAction[player, command[i]]; ENDLOOP; command _ commands[visitors]; FOR i: CARDINAL IN [0..6) DO player _ @game.player[Add[VQB, i]]; IF game.state IN [offField..setUp] THEN { IF command[i].action # run THEN LOOP; [x, y] _ GetTarget[command[i].target, player.position]; Move[player, x, y, TRUE]; LOOP}; PlayAction[player, command[i]]; ENDLOOP; ENDLOOP; END; ballRange: REAL _ 100; tackle: REAL _ .7; block: REAL _ 2.5; zone: REAL _ 10; PlayAction: PROC[player: Player, command: Command] = BEGIN x, y: REAL; target: Target; action: Action; possessor: BOOLEAN; nearest: Position _ none; i: Position _ player.position; possessor _ (TeamOf[i] = game.possessor); target _ command.target; action _ command.action; IF action # block THEN player.blocking _ none; IF action = pass OR action = kick THEN { IF game.ballCarrier # i THEN RETURN; IF action = kick THEN target _ [goal[]]; [x, y] _ GetTarget[target, i]; Pass[player, x, y, action = pass]; RETURN}; IF game.possessor # TeamOf[i] AND game.ballCarrier # none AND Catch[player, game.ballCarrier, IF Blocked[player] THEN .4 ELSE .7] THEN { PlayOver[game.player[game.ballCarrier].x]; RETURN}; IF action = stop THEN RETURN; IF action = block THEN WITH t: target SELECT FROM player => IF possessor THEN { IF t.position = none THEN t.position _ Nearest[player, player.x, player.y, block]; IF t.position = none THEN RETURN; IF Catch[player, t.position, 2] THEN player.blocking _ t.position}; relative, absolute, goal => { nearest: Position _ none; [x, y] _ GetTarget[target, i]; IF possessor THEN IF ABS[player.x - x] < block AND ABS[player.y - y] < block THEN nearest _ Nearest[player, x, y, block] ELSE nearest _ Nearest[player, player.x, player.y, 1]; IF ~possessor THEN nearest _ Nearest[player, x, y, zone]; target _ IF nearest = none THEN [absolute[x, y]] ELSE [player[nearest]]; IF possessor AND nearest # none AND Catch[player, nearest, 2] THEN player.blocking _ nearest}; ENDCASE; [x, y] _ GetTarget[target, i]; WITH t: target SELECT FROM player => IF t.position = ball THEN Move[player, x, y, game.ballCarrier = none] ELSE IF ~possessor AND t.position # game.ballCarrier THEN Move[player, x, y, FALSE, t.position] ELSE Move[player, x, y]; goal => Move[player, x, y]; relative, absolute => Move[player, x, y, TRUE]; ENDCASE; END; inertia: REAL _ 4; Move: PROC[player: Player, x, y: REAL, stopping: BOOLEAN _ FALSE, receiver: Position _ none] = BEGIN ax, ay: REAL; rt, speed: REAL; ax _ x - player.x; ay _ y - player.y; speed _ IF TeamOf[player.position] # game.possessor THEN .22 ELSE .2; rt _ RealFns.SqRt[ax*ax + ay*ay]; stopping _ stopping AND rt < .5; IF stopping THEN {player.dx _ 0; player.dy _ 0}; IF rt # 0 THEN IF stopping AND rt < speed THEN {player.dx _ player.dx + ax; player.dy _ player.dy + ay} ELSE {player.dx _ player.dx + speed*ax/rt; player.dy _ player.dy + speed*ay/rt}; IF receiver # none AND rt < 2 THEN {-- decelerate player.dx _ player.dx/2; player.dy _ player.dy/2}; rt _ RealFns.SqRt[player.dx*player.dx + player.dy*player.dy]; IF rt > inertia*speed THEN { player.dx _ player.dx*inertia*speed/rt; player.dy _ player.dy*inertia*speed/rt}; ax _ player.x + player.dx/inertia; ay _ player.y + player.dy/inertia; IF game.state < option OR ax IN [-11..111] THEN player.x _ ax; IF game.state < option OR ay IN [-1..fieldWidth+1] THEN player.y _ ay; IF player.position = game.ballCarrier THEN { game.player[ball].x _ player.x; game.player[ball].y _ player.y; game.player[ball].dx _ player.dx; game.player[ball].dy _ player.dy}; CheckBlocks[player]; END; GetTarget: PROCEDURE[target: Target, pos: Position] RETURNS[tx, ty: REAL] = BEGIN otherTeam: Team = IF TeamOf[pos] = home THEN visitors ELSE home; goal: INTEGER = Goal[otherTeam, game.quarter, TRUE]; WITH t: target SELECT FROM goal => RETURN[goal, game.player[pos].y]; player => IF t.position = ball AND game.ballCarrier = none AND game.state = pass THEN WITH bt: game.ballCatchable SELECT FROM absolute => RETURN[bt.x, bt.y]; ENDCASE => ERROR ELSE { m: REAL; p1, p2: Player _ NIL; p1 _ @game.player[pos]; p2 _ @game.player[t.position]; IF p2.blocking = pos THEN RETURN[p2.x, p2.y]; IF ABS[p2.dx] < .1 AND ABS[p2.dy] < .1 THEN RETURN[p2.x, p2.y]; m _ 2*(p2.dx*(p1.x - p2.x) + p2.dy*(p1.y - p2.y)); IF m <= 0 THEN RETURN[p2.x, p2.y]; m _ ((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y))/m; RETURN[p2.x + m*p2.dx, p2.y + m*p2.dy]}; 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; END; CheckBlocks: PROCEDURE[player: Player] = INLINE BEGIN -- takes blocking into account IF game.state < option THEN RETURN; FOR i: Position IN Players DO IF i = player.position THEN LOOP; IF i = game.ballCarrier THEN LOOP; Block[player, @game.player[i]] ENDLOOP; END; Block: PROCEDURE[player1, player2: Player] = BEGIN delta: INTEGER _ 1; IF player1.blocking = player2.position THEN delta _ 2; IF player2.blocking = player1.position THEN delta _ 2; IF ABS[player1.x - player2.x] > delta THEN RETURN; IF ABS[player1.y - player2.y] > delta THEN RETURN; player1.dx _ player2.dx _ (player1.dx + player2.dx)/2; player1.dy _ player2.dy _ (player1.dy + player2.dy)/2; END; Blocked: PROCEDURE[player: Player] RETURNS[BOOLEAN] = BEGIN FOR i: Position IN Players DO IF TeamOf[i] = TeamOf[player.position] THEN LOOP; IF game.player[i].blocking = player.position THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; Nearest: PROC[player: Player, x, y: REAL, delta: REAL _ 15] RETURNS[nearest: Position _ none] = BEGIN blocked: Position _ none; bx, by: REAL _ 100; nx, ny: REAL _ 100; CheckNear: PROCEDURE[other: Player] = { dx, dy: REAL; IF ABS[other.x - x] > delta THEN RETURN; IF ABS[other.y - y] > delta THEN RETURN; IF TeamOf[other.position] = game.offense AND ~CrossLine[other.x, game.scrimmage, game.offense, game.quarter] THEN RETURN; dx _ ABS[other.x - player.x]; dy _ ABS[other.y - player.y]; IF Blocked[other] THEN {IF dx < bx AND dy < by THEN {bx _ dx; by _ dy; blocked _ other.position}} ELSE {IF dx < nx AND dy < ny THEN {nx _ dx; ny _ dy; nearest _ other.position}}}; IF player.position IN HomeTeam THEN FOR i: Position IN VisitorsTeam DO CheckNear[@game.player[i]]; ENDLOOP ELSE FOR i: Position IN HomeTeam DO CheckNear[@game.player[i]]; ENDLOOP; IF nearest = none THEN nearest _ blocked; END; Add: PROCEDURE[p: Position, i: CARDINAL] RETURNS[Position] = INLINE {RETURN[LOOPHOLE[LOOPHOLE[p, CARDINAL]+i]]}; Pass: PROCEDURE[player: Player, x, y: REAL, pass: BOOLEAN] = BEGIN team: Team; delta, x1, y1: REAL; team _ TeamOf[player.position]; grounded _ IF pass THEN PossibleGround[player, team] ELSE FALSE; IF grounded THEN passerX _ player.x; game.lateral _ FALSE; IF pass AND game.state = run THEN -- run => ballCarrier crossed line of scrimmage IF CrossLine[x, player.x, team, game.quarter] THEN game.penalties[team] _ forwardPass -- illegal forward pass ELSE game.lateral _ TRUE; delta _ RealFns.SqRt[(x - player.x)*(x - player.x) + (y - player.y)*(y - player.y)]; IF delta > 50 THEN { x _ player.x + 50*(x-player.x)/delta; y _ player.y + 50*(y-player.y)/delta; delta _ 50}; IF delta < .01 THEN RETURN; game.ballRange _ ballRange/delta; x1 _ x + game.ballRange*(x - player.x)/(2*delta); y1 _ y + game.ballRange*(y - player.y)/(2*delta); game.ballTarget _ [absolute[x1, y1]]; x1 _ x - game.ballRange*(x - player.x)/(2*delta); y1 _ y - game.ballRange*(y - player.y)/(2*delta); IF game.ballRange < delta THEN game.ballCatchable _ [absolute[x1, y1]] ELSE game.ballCatchable _ [absolute[player.x, player.y]]; game.ballRange _ game.ballRange + 2; game.ballCarrier _ none; game.state _ pass; game.pass _ pass; END; PossibleGround: PROCEDURE[player: Player, team: Team] RETURNS[grounded: BOOLEAN] = BEGIN grounded _ NOT CrossLine[player.x, game.scrimmage, team, game.quarter]; IF game.possessor # game.offense THEN grounded _ FALSE; passerX _ player.x; IF grounded THEN FOR i: Position IN Players DO IF TeamOf[i] = TeamOf[player.position] THEN LOOP; IF ABS[game.player[i].x - player.x] > 3 THEN LOOP; IF ABS[game.player[i].y - player.y] > 3 THEN LOOP; RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; END; passerX: REAL; ballSpeed: REAL _ 1; grounded: BOOLEAN _ FALSE; LiveBall: PROC = BEGIN x, y: REAL; time: System.Pulses; game.state _ liveBall; IF game.ballRange = 0 THEN RETURN; time _ System.GetClockPulses[]; SELECT time MOD 3 FROM 0 => x _ -1; 1 => x _ 0; ENDCASE => x _ 1; SELECT time/3 MOD 3 FROM 0 => y _ -1; 1 => y _ 0; ENDCASE => y _ 1; x _ game.player[ball].x + 50*x/game.ballRange; y _ game.player[ball].y + 50*y/game.ballRange; game.ballRange _ 2*game.ballRange/3; IF game.ballRange < 1 THEN game.ballRange _ 0; game.ballTarget _ game.ballCatchable _ [absolute[x, y]]; END; MoveBall: PROC[player: Player, x, y: REAL] = BEGIN rt, dx, dy: REAL; dx _ x - player.x; dy _ y - player.y; IF dx = 0 AND dy = 0 THEN RETURN; rt _ RealFns.SqRt[dx*dx + dy*dy]; player.x _ player.x + (IF rt <= ballSpeed THEN dx ELSE dx*ballSpeed/rt); player.y _ player.y + (IF rt <= ballSpeed THEN dy ELSE dy*ballSpeed/rt); IF game.state = pass AND rt < game.ballRange THEN game.state _ catchable; IF grounded AND game.state = catchable THEN FOR i: Position IN Players DO IF TeamOf[i] # game.possessor THEN LOOP; IF ABS[game.player[i].x - game.player[ball].x] > 4 THEN LOOP; IF ABS[game.player[i].y - game.player[ball].y] > 4 THEN LOOP; grounded _ FALSE; ENDLOOP; END; CatchBall: PROC = BEGIN delta: REAL _ 10; receiver, ball: Player; closest: Position _ none; IF game.state = pass THEN RETURN; ball _ @game.player[ball]; FOR i: Position IN Players DO receiver _ @game.player[i]; IF ~Catch[receiver, ball, 1.1] THEN LOOP; IF ABS[receiver.x - ball.x] + ABS[receiver.y - ball.y] < delta THEN { delta _ ABS[receiver.x - ball.x] + ABS[receiver.y - ball.y]; closest _ i}; ENDLOOP; IF closest = none THEN RETURN; IF game.state # option AND ~game.pass AND TeamOf[closest] = game.offense THEN {PlayOver[game.player[closest].x, TRUE]; RETURN}; -- grounding a punt IF game.state # option THEN game.state _ run; game.ballCarrier _ closest; game.possessor _ TeamOf[closest]; IF game.possessor # game.offense THEN game.state _ turnover; END; Catch: PROCEDURE[catcher: Player, catchee: Position, delta: REAL _ 1] RETURNS[BOOLEAN] = BEGIN x, y: REAL; IF catchee = ball THEN catchee _ game.ballCarrier; IF ABS[catcher.y - game.player[catchee].y] > delta THEN RETURN[FALSE]; IF ABS[catcher.x - game.player[catchee].x] > delta THEN RETURN[FALSE]; IF catchee = ball AND Blocked[catcher] THEN RETURN[FALSE]; IF catchee = ball THEN { -- has to be moving toward the receiver [x, y] _ GetTarget[game.ballTarget, none]; IF x = game.player[ball].x AND y = game.player[ball].y THEN RETURN[TRUE]; IF x < catcher.x AND catcher.x < game.player[ball].x THEN RETURN[TRUE]; IF x > catcher.x AND catcher.x > game.player[ball].x THEN RETURN[TRUE]; IF y < catcher.y AND catcher.y < game.player[ball].y THEN RETURN[TRUE]; IF y > catcher.y AND catcher.y > game.player[ball].y THEN RETURN[TRUE]; RETURN[FALSE]}; RETURN[TRUE]; END; PlayOver: PROCEDURE[scrimmage: REAL, stopClock: BOOLEAN _ FALSE] = BEGIN game.clockStopped _ stopClock; IF game.ballCarrier = none AND game.pass AND ~game.lateral THEN scrimmage _ game.scrimmage; scrimmage _ HandlePenalties[scrimmage]; IF EndZone[scrimmage] THEN Score[scrimmage]; IF game.clock <= 0 THEN { game.clock _ 15*60; game.clockStopped _ TRUE; game.quarter _ game.quarter + 1; IF game.quarter = 3 OR game.quarter = 5 THEN {OffField[]; game.timeouts _ [4, 4]; RETURN}; scrimmage _ 100 - scrimmage; game.scrimmage _ 100 - game.scrimmage; game.firstDownMarker _ 100 - game.firstDownMarker}; IF EndZone[scrimmage] THEN {Wait[60]; KickOff[Opponent[game.possessor]]; RETURN}; game.scrimmage _ scrimmage; IF game.possessor # game.offense THEN { game.offense _ game.possessor; FirstDown[]}; IF CrossLine[scrimmage, game.firstDownMarker, game.offense, game.quarter] THEN FirstDown[]; IF game.down = 5 THEN { game.offense _ Opponent[game.offense]; game.possessor _ game.offense; FirstDown[]}; Wait[60]; Huddle[]; END; HandlePenalties: PROCEDURE[scrimmage: REAL] RETURNS[REAL] = BEGIN IF grounded AND game.possessor # game.offense THEN grounded _ FALSE; -- interception IF grounded THEN {game.penalties[game.offense] _ grounding; grounded _ FALSE}; IF game.penalties[home] = offSides AND game.penalties[visitors] = offSides THEN {game.penaltyState _ canceled; RETURN[game.scrimmage]}; IF game.penalties[home] # none AND game.penalties[visitors] # none THEN {game.down _ game.down + 1; game.penaltyState _ canceled; RETURN[game.scrimmage]}; FOR team: Team IN Team DO SELECT game.penalties[team] FROM none => NULL; offSides => RETURN[SetPenalty[team, 5]]; delayOfGame => RETURN[IF Accepted[] THEN SetPenalty[team, 5] ELSE scrimmage]; forwardPass => IF Accepted[] THEN RETURN[SetPenalty[team, 15]]; grounding => IF Accepted[] THEN RETURN[SetPenalty[team, 15]]; ENDCASE => IF Accepted[] THEN RETURN[SetPenalty[team, 5]]; ENDLOOP; game.down _ game.down + 1; RETURN[scrimmage]; END; Accepted: PROCEDURE RETURNS[BOOLEAN] = BEGIN game.penaltyState _ asking; WHILE game.penaltyState = asking DO Wait[10]; ENDLOOP; RETURN[game.penaltyState = accepted]; END; SetPenalty: PROCEDURE[team: Team, yards: REAL] RETURNS[line: REAL] = BEGIN game.penaltyState _ accepted; IF Direction[team, game.quarter] = 1 THEN IF game.scrimmage <= yards THEN RETURN[(game.scrimmage+1)/2] ELSE RETURN[game.scrimmage - yards] ELSE IF (100 - game.scrimmage) <= yards THEN RETURN[(100 + game.scrimmage)/2] ELSE RETURN[game.scrimmage + yards]; END; Score: PROCEDURE[scrimmage: REAL] = BEGIN goal: INTEGER; opponent: Team; game.clockStopped _ TRUE; goal _ Goal[game.possessor, game.quarter]; opponent _ Opponent[game.possessor]; IF game.ballCarrier = none THEN { -- check for field goal IF scrimmage IN [-9..109] THEN RETURN; IF game.player[ball].y NOT IN [fieldWidth/3..2*fieldWidth/3] THEN RETURN}; SELECT TRUE FROM game.ballCarrier = none AND goal > 50 AND scrimmage < 50 => game.score[game.possessor] _ game.score[game.possessor] + 3; game.ballCarrier = none AND goal < 50 AND scrimmage > 50 => game.score[game.possessor] _ game.score[game.possessor] + 3; goal > 50 AND scrimmage < 50 => game.score[game.possessor] _ game.score[game.possessor] + 7; goal < 50 AND scrimmage > 50 => game.score[game.possessor] _ game.score[game.possessor] + 7; goal > 50 AND scrimmage > 50 => game.score[opponent] _ game.score[opponent] + 2; goal < 50 AND scrimmage < 50 => game.score[opponent] _ game.score[opponent] + 2; ENDCASE; END; SetOffField: PROC = BEGIN player: Player; command: ARRAY [0..6) OF Command; command _ offField1; FOR i: CARDINAL IN [0..6) DO player _ @game.player[Add[HQB, i]]; WITH t: command[i].target SELECT FROM absolute => {player.x _ t.x; player.y _ t.y}; ENDCASE => ERROR; ENDLOOP; command _ offField2; FOR i: CARDINAL IN [0..6) DO player _ @game.player[Add[VQB, i]]; WITH t: command[i].target SELECT FROM absolute => {player.x _ t.x; player.y _ t.y}; ENDCASE => ERROR; ENDLOOP; OffField[]; END; offField1: Commands = [ [run, [absolute[23, 55]]], [run, [absolute[26, 55]]], [run, [absolute[29, 55]]], [run, [absolute[32, 55]]], [run, [absolute[35, 55]]], [run, [absolute[38, 55]]]]; offField2: Commands = [ [run, [absolute[77, -5]]], [run, [absolute[74, -5]]], [run, [absolute[71, -5]]], [run, [absolute[68, -5]]], [run, [absolute[65, -5]]], [run, [absolute[62, -5]]]]; Init[]; END . . . êFootballControl.mesa Last Edited by: Maxwell, March 15, 1983 10:21 am ****************************************************************** RPC procedure ****************************************************************** ****************************************************************** Control procedures ****************************************************************** move the ball back so that the center won't catch it check for offSides and illegal formations ****************************************************************** Main Loop ****************************************************************** check clock and penalties is the ball now out of play? move the ball are we committed to a run? move home team move visitors team check for passing or kicking try to tackle the ball carrier look for someone to guard or block move the player ****************************************************************** utility procedures ****************************************************************** accelerate towards the target there is an upper bound on the velocity blocked: BOOLEAN _ TRUE; IF (player2.x - player1.x > 0) = (player1.dx < 0) THEN blocked _ FALSE; IF (player2.y - player1.y > 0) = (player1.dy < 0) THEN blocked _ FALSE; IF blocked THEN { IF (player1.x - player2.x > 0) = (player2.dx < 0) THEN blocked _ FALSE; IF (player1.y - player2.y > 0) = (player2.dy < 0) THEN blocked _ FALSE}; IF ~blocked THEN RETURN; ****************************************************************** dealing with the ball ****************************************************************** check for penalties throw the ball the ball's target is half of the range after [x,y] the ball is catchable half of the range before [x,y] select a random direction move the ball in that direction ****************************************************************** end of play computations ****************************************************************** check for incomplete pass, penalties, and scoring did the gun go off? was there a turnover on the play? did they make the critical first down? double penalties single penalties ****************************************************************** simple plays ****************************************************************** ÊŸ˜Jšœ™Jšœ0™0J˜šÏk ˜ J˜J˜Jšœœ&˜DJšœœ˜Jšœœ˜Jšœœœ˜Jšœœ ˜JšœœC˜OJšœ œ˜$—J˜šœœ˜Jšœ=œ˜YJšœ#˜*—Jšœœ˜J˜JšœB™BJšœ ™ JšœB™BJ˜Jšœ œœ˜J˜š Ïn œœœ œ œ˜6Jš˜Jšœœ˜Jš œ œœœœ œ˜4Jšœ œ˜J˜1˜)J˜ J˜ Jšœ œ˜!—Jšœ˜J˜—šž œœœ œ˜$Jš˜Jš œ œœœœ œ˜2Jš œ œœœœ˜+J˜-Jšœ˜J˜—šžœœ˜ Jš˜Jšœœ˜šœ œ ˜J˜Jšœ˜—Jšœ˜J˜—J™JšœB™BJšœ™JšœB™BJ˜J˜ Jšœœœ˜Jš œ œœ œœ˜RJ˜šž œœœ˜2Jšœ ˜Jš˜J˜Jšœ˜Jšœ˜J˜—šžœ œ˜Jš˜J˜J˜J˜J˜J˜)Jšœœ˜Jšœ˜J˜—šžœœ œ˜'Jš˜šœœ˜J˜J˜—Jšœœœ ˜Jšœœœœ˜Fšœ$œ˜,J˜J˜J˜!J˜"—J˜Jšœ˜J˜—šž œ œ œ œ˜KJš˜Jšœœœ œ˜@Jšœœ!œ˜4šœ œ˜Jšœœ˜)šœ œœœ˜Pšœœœœ˜-Jšœ œœ˜0—šœ˜Jšœœ˜Jšœœ˜J˜J˜Jšœœœ ˜-Jš œœ œœ œœ ˜?J˜2Jšœœœ ˜"J˜BJšœ"˜(——š œ œœ œœ˜GJ˜—Jšœ œ ˜Jšœœ˜—Jšœ˜J˜—šž œ œ˜(JšœœŸ˜+Jšœœœ˜#šœ œ ˜Jšœœœ˜!Jšœœœ˜"J˜Jšœ˜—Jšœ˜J˜—šžœ œ˜,Jš˜Jšœœ˜Jšœ™Jšœ%œ ˜6Jšœ%œ ˜6Jšœœ œœ˜2Jšœœ œœ˜2JšœH™HJšœG™Gšœ™JšœH™HJšœH™H—Jšœ™J˜6J˜6Jšœ˜J˜—šžœ œœœ˜5Jš˜šœ œ ˜Jšœ%œœ˜1Jšœ+œœœ˜?Jšœ˜—Jšœœ˜Jšœ˜J˜—šžœœœ œ˜