DIRECTORY BasicTime USING [GMT, Now, nullGMT], FootballInternal, FootballMaster, FootballMasterRpcControl USING [ExportInterface, UnexportInterface], Process USING [SetTimeout], Random USING [Create, ChooseInt, RandomStream], RealFns USING [SqRt], Rope USING [ROPE], RPC USING [MakeKey], UserCredentials USING [Get]; FootballControl: MONITOR IMPORTS BasicTime, FootballMaster, FootballMasterRpcControl, Process, Random, RealFns, RPC, UserCredentials EXPORTS FootballInternal, FootballMaster = { OPEN FootballMaster; StupidFuckingWarnings: PUBLIC SIGNAL [itsGottaHaveAGoddamName: ATOM] = CODE; process: PROCESS _ NIL; StartServer: PUBLIC ENTRY PROC [name: Rope.ROPE] = { user, password: Rope.ROPE; IF stopped = FALSE THEN RETURN ELSE stopped _ FALSE; process _ FORK Main[]; [user, password] _ UserCredentials.Get[]; FootballMasterRpcControl.ExportInterface[ interfaceName: [instance: name], user: user, password: RPC.MakeKey[password]]; }; StopServer: PUBLIC ENTRY PROC = { IF stopped = TRUE OR process = NIL THEN RETURN ELSE stopped _ TRUE; WHILE process # NIL DO WAIT timer; ENDLOOP; FootballMasterRpcControl.UnexportInterface[]; }; Init: PROC = { controlGame _ NEW[GameRec _ []]; FOR i: Position IN Players DO controlGame.player[i].position _ i; ENDLOOP; }; controlGame: Game; slow: BOOL _ FALSE; commands: ARRAY Team OF Commands _ [ALL[[stop, [null[]]]], ALL[[stop, [null[]]]]]; SetCommands: PUBLIC PROC [team: Team, c: Commands] RETURNS [GameRec] = { commands[team] _ c; RETURN [controlGame^]; }; OffField: PROC = { controlGame.state _ offField; controlGame.ballCarrier _ none; controlGame.player[ball].x _ -100; controlGame.player[ball].y _ -100; controlGame.ballTarget _ [absolute[-100, -100]]; controlGame.clockStopped _ TRUE; }; KickOff: PUBLIC PROC [team: Team] = { IF controlGame.quarter = 5 THEN { controlGame.quarter _ 1; controlGame.score _ [0, 0]}; IF controlGame.quarter = 1 AND controlGame.clock = 15*60 THEN team _ home; IF controlGame.quarter = 3 AND controlGame.clock = 15*60 THEN team _ visitors; controlGame.offense _ controlGame.possessor _ team; IF Goal[team, controlGame.quarter] < 50 THEN controlGame.scrimmage _ 20 ELSE controlGame.scrimmage _ 80; FirstDown[]; Huddle[]; }; Huddle: PROC = { FOR i: Position IN Players DO controlGame.player[i].dx _ 0; controlGame.player[i].dy _ 0; ENDLOOP; controlGame.player[ball].x _ controlGame.scrimmage; controlGame.player[ball].y _ fieldWidth/2; controlGame.ballCarrier _ none; controlGame.ballTarget _ [absolute[controlGame.scrimmage, fieldWidth/2]]; controlGame.state _ huddle; controlGame.pass _ FALSE; controlGame.lateral _ FALSE; delay _ 1; startHuddle _ controlGame.clock; }; SetUp: PUBLIC PROC [team: Team] = { IF controlGame.state = offField THEN KickOff[team]; controlGame.state _ setUp; }; Hike: PUBLIC PROC [team: Team] = { hike: REAL; count: NAT _ 0; IF team # controlGame.offense THEN RETURN; IF ~Ready[home] OR ~Ready[visitors] THEN RETURN; delay _ IF slow THEN 5 ELSE 1; controlGame.penalties _ [none, none]; controlGame.penaltyState _ none; controlGame.clockStopped _ FALSE; hike _ controlGame.scrimmage - Direction[controlGame.offense, controlGame.quarter]*15; controlGame.player[ball].x _ controlGame.scrimmage - Direction[controlGame.offense, controlGame.quarter]*1.1; controlGame.ballTarget _ [absolute[hike, fieldWidth/2]]; controlGame.ballCarrier _ ball; FOR i: Position IN Players DO player: Player = @controlGame.player[i]; team: Team = TeamOf[i]; player.blocking _ none; -- reset SELECT TRUE FROM team # controlGame.offense => { }; ABS[player.dx]+ABS[player.dy] < 1.0 => { }; count = 0 => count _ 1; ENDCASE => controlGame.penalties[controlGame.offense] _ formation; IF CrossLine[player.x, controlGame.scrimmage, team, controlGame.quarter] THEN controlGame.penalties[team] _ offSides; ENDLOOP; controlGame.state _ option; }; Ready: PROC [team: Team] RETURNS [BOOL] = { count: NAT _ 0; FOR i: Position IN Players DO IF TeamOf[i] # team THEN LOOP; IF ABS[controlGame.player[i].x - controlGame.scrimmage] < 2 THEN count _ count + 1; ENDLOOP; RETURN [count > 2]; }; FirstDown: PROC = { controlGame.down _ 1; IF Goal[controlGame.offense, controlGame.quarter] < 50 THEN controlGame.firstDownMarker _ MIN[100, controlGame.scrimmage + 10] ELSE controlGame.firstDownMarker _ MAX[0, controlGame.scrimmage - 10]; }; TimeOut: PUBLIC PROC [team: Team] = { IF controlGame.timeouts[team] = 0 THEN RETURN; IF controlGame.state > option THEN RETURN; IF controlGame.clockStopped THEN RETURN; controlGame.clockStopped _ TRUE; controlGame.timeouts[team] _ controlGame.timeouts[team] - 1; }; PenaltyResponse: PUBLIC PROC [team: Team, accepted: BOOL] = { IF controlGame.penalties[Opponent[team]] = none THEN RETURN; IF controlGame.penaltyState # asking THEN RETURN; controlGame.penaltyState _ IF accepted THEN accepted ELSE declined; }; DisableDelayOfGame: PUBLIC PROC = {delayOfGame _ FALSE}; timer: CONDITION; delay: CARDINAL _ 1; stopped: BOOL _ TRUE; gmt: BasicTime.GMT _ BasicTime.nullGMT; startHuddle: INTEGER _ 0; delayOfGame: BOOL _ TRUE; Wait: ENTRY PROC [i: CARDINAL] ={ DO IF i = 0 THEN EXIT ELSE i _ i - 1; WAIT timer; ENDLOOP; }; Main: PROC = { x, y: REAL; player: Player; command: Commands; Process.SetTimeout[@timer, 1]; SetOffField[]; DO Wait[delay]; IF stopped THEN {process _ NIL; EXIT}; IF ~controlGame.clockStopped AND controlGame.quarter # 5 THEN IF BasicTime.Now[] # gmt THEN { controlGame.clock _ controlGame.clock - 1; gmt _ BasicTime.Now[]}; IF controlGame.clock <= 0 AND controlGame.state < option THEN PlayOver[controlGame.scrimmage]; IF delayOfGame AND controlGame.state < option AND startHuddle - controlGame.clock > 60 THEN { readyHome: BOOL = Ready[home]; readyVisitors: BOOL = Ready[visitors]; SELECT TRUE FROM ~readyHome AND ~readyVisitors => {startHuddle _ controlGame.clock; LOOP}; ~readyHome => controlGame.penalties[home] _ delayOfGame; ~readyVisitors => controlGame.penalties[visitors] _ delayOfGame; ENDCASE => controlGame.penalties[controlGame.offense] _ delayOfGame; PlayOver[controlGame.scrimmage, TRUE]}; IF (controlGame.penalties[home] = offSides OR controlGame.penalties[visitors] = offSides) AND controlGame.state > setUp THEN PlayOver[controlGame.scrimmage, TRUE]; x _ controlGame.player[ball].x; y _ controlGame.player[ball].y; IF controlGame.state > setUp AND (x NOT IN [-10..110] OR y NOT IN [0..fieldWidth]) THEN PlayOver[x, TRUE]; IF controlGame.state > setUp AND controlGame.ballCarrier # ball AND Direction[TeamOf[controlGame.ballCarrier], controlGame.quarter] = 1 AND x > 100 THEN PlayOver[101, TRUE]; IF controlGame.state > setUp AND controlGame.ballCarrier # ball AND Direction[TeamOf[controlGame.ballCarrier], controlGame.quarter] = -1 AND x < 0 THEN PlayOver[-1, TRUE]; IF controlGame.state > setUp AND controlGame.ballCarrier = none THEN { [x, y] _ GetTarget[controlGame.ballTarget, none]; MoveBall[@controlGame.player[ball], x, y]; CatchBall[]; IF controlGame.player[ball].x = x AND controlGame.player[ball].y = y THEN -- ball has hit the ground IF controlGame.pass AND ~controlGame.lateral THEN PlayOver[controlGame.scrimmage, TRUE] ELSE controlGame.state _ liveBall}; IF controlGame.state = option AND CrossLine[x, controlGame.scrimmage, controlGame.offense, controlGame.quarter] THEN controlGame.state _ run; command _ commands[home]; FOR i: CARDINAL IN [0..6) DO player _ @controlGame.player[Add[HQB, i]]; IF controlGame.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 _ @controlGame.player[Add[VQB, i]]; IF controlGame.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; }; ballRange: REAL _ 100; tackle: REAL _ .7; block: REAL _ 2.5; zone: REAL _ 10; PlayAction: PROC [player: Player, command: Command] = { x, y: REAL; nearest: Position _ none; i: Position _ player.position; possessor: BOOL _ (TeamOf[i] = controlGame.possessor); target: Target _ command.target; action: Action _ command.action; IF action # block THEN player.blocking _ none; IF action = pass OR action = kick THEN { IF controlGame.ballCarrier # i THEN RETURN; IF action = kick THEN target _ [goal[]]; [x, y] _ GetTarget[target, i]; Pass[player, x, y, action = pass]; RETURN}; IF controlGame.possessor # TeamOf[i] AND controlGame.ballCarrier # none AND Catch[player, controlGame.ballCarrier, IF Blocked[player] THEN .4 ELSE .7] THEN { PlayOver[controlGame.player[controlGame.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, controlGame.ballCarrier = none] ELSE IF ~possessor AND t.position # controlGame.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; }; inertia: REAL _ 4; tipsiness: REAL _ 0.01; Move: PROC [player: Player, x, y: REAL, stopping: BOOL _ FALSE, receiver: Position _ none] = { ax: REAL _ x - player.x; ay: REAL _ y - player.y; teamHasBall: BOOL _ TeamOf[player.position] = controlGame.possessor; speed: REAL _ IF teamHasBall THEN .20 ELSE .22; speedProd: REAL _ inertia*speed; rt: REAL _ 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 { player.dx _ player.dx/2; player.dy _ player.dy/2; }; rt _ RealFns.SqRt[player.dx*player.dx + player.dy*player.dy]; IF NOT teamHasBall THEN { FOR i: Position IN Players DO otherGuy: Player _ @controlGame.player[i]; IF TeamOf[i] = TeamOf[player.position] THEN LOOP; IF otherGuy.blocking = player.position THEN { IF DistSquared[otherGuy, player] <= 1.0 THEN { ball: Player _ @controlGame.player[ball]; IF DistSquared[otherGuy, ball] < DistSquared[player, ball] THEN { speedProd _ speedProd * 0.5; EXIT; }; }; }; ENDLOOP; }; IF rt > speedProd THEN { temp: REAL = speedProd/rt; player.dx _ player.dx*temp; player.dy _ player.dy*temp; }; { tip: REAL _ IF controlGame.state < option THEN 0.0 ELSE tipsiness; cx: REAL _ (Random.ChooseInt[rs, 0, 10] - 5) * tip; cy: REAL _ (Random.ChooseInt[rs, 0, 10] - 5) * tip; ax _ player.x + player.dx/inertia + cx; ay _ player.y + player.dy/inertia + cy; }; IF controlGame.state < option OR ax IN [-11..111] THEN player.x _ ax; IF controlGame.state < option OR ay IN [-1..fieldWidth+1] THEN player.y _ ay; IF player.position = controlGame.ballCarrier THEN { controlGame.player[ball].x _ player.x; controlGame.player[ball].y _ player.y; controlGame.player[ball].dx _ player.dx; controlGame.player[ball].dy _ player.dy}; IF controlGame.state >= option THEN FOR i: Position IN Players DO IF i = player.position THEN LOOP; IF i = controlGame.ballCarrier THEN LOOP; Block[player, @controlGame.player[i]] ENDLOOP; }; DistSquared: PROC [player1, player2: Player] RETURNS [REAL] = { dx: REAL _ player2.x-player1.x; dy: REAL _ player2.y-player1.y; RETURN [dx*dx + dy*dy]; }; GetTarget: PROC [target: Target, pos: Position] RETURNS [tx, ty: REAL] = { otherTeam: Team = IF TeamOf[pos] = home THEN visitors ELSE home; goal: INTEGER = Goal[otherTeam, controlGame.quarter, TRUE]; WITH t: target SELECT FROM goal => RETURN [goal, controlGame.player[pos].y]; player => IF t.position = ball AND controlGame.ballCarrier = none AND controlGame.state = pass THEN WITH bt: controlGame.ballCatchable SELECT FROM absolute => RETURN [bt.x, bt.y]; ENDCASE => ERROR ELSE { m: REAL; p1, p2: Player _ NIL; p1 _ @controlGame.player[pos]; p2 _ @controlGame.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 [controlGame.scrimmage + (IF goal > 50 THEN t.x ELSE -t.x), fieldWidth/2 + t.y]; absolute => RETURN [t.x, t.y]; ENDCASE => ERROR; }; Block: PROC [player1, player2: Player] = { 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; }; Blocked: PROC [player: Player] RETURNS [BOOL] = { FOR i: Position IN Players DO IF TeamOf[i] = TeamOf[player.position] THEN LOOP; IF controlGame.player[i].blocking = player.position THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; Nearest: PROC [player: Player, x, y: REAL, delta: REAL] RETURNS [nearest: Position _ none] = { blocked: Position _ none; bx, by: REAL _ 100; nx, ny: REAL _ 100; CheckNear: PROC [other: Player] = { dx: REAL _ ABS[other.x - x]; dy: REAL _ ABS[other.y - y]; IF dx > delta THEN RETURN; IF dy > delta THEN RETURN; IF TeamOf[other.position] = controlGame.offense AND ~CrossLine[other.x, controlGame.scrimmage, controlGame.offense, controlGame.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[@controlGame.player[i]]; ENDLOOP ELSE FOR i: Position IN HomeTeam DO CheckNear[@controlGame.player[i]]; ENDLOOP; IF nearest = none THEN nearest _ blocked; }; Add: PROC [p: Position, i: CARDINAL] RETURNS [Position] = INLINE { RETURN [LOOPHOLE[LOOPHOLE[p, CARDINAL]+i]]; }; Pass: PROC [player: Player, x, y: REAL, pass: BOOL] = { 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; controlGame.lateral _ FALSE; IF pass AND controlGame.state = run THEN IF CrossLine[x, player.x, team, controlGame.quarter] THEN controlGame.penalties[team] _ forwardPass -- illegal forward pass ELSE controlGame.lateral _ TRUE; x1 _ x - player.x; y1 _ y - player.y; delta _ RealFns.SqRt[x1*x1 + y1*y1]; IF delta > 50 THEN { d50: REAL = 50/delta; x _ player.x + x1*d50; y _ player.y + y1*d50; delta _ 50; }; IF delta > .01 THEN { temp: REAL = controlGame.ballRange/(2*delta); rem: REAL _ delta; WHILE rem > 10 DO nx,ny: REAL; [nx,ny] _ RandomDelta[ballRandom]; x _ x + nx; y _ y + ny; rem _ rem - 10; ENDLOOP; controlGame.ballRange _ ballRange/delta; x1 _ x + temp*(x - player.x); y1 _ y + temp*(y - player.y); controlGame.ballTarget _ [absolute[x1, y1]]; IF controlGame.ballRange < delta THEN controlGame.ballCatchable _ [absolute[x1, y1]] ELSE controlGame.ballCatchable _ [absolute[player.x, player.y]]; controlGame.ballRange _ controlGame.ballRange + 2; controlGame.ballCarrier _ none; controlGame.state _ pass; controlGame.pass _ pass; }; }; PossibleGround: PROC [player: Player, team: Team] RETURNS [grounded: BOOL] = { grounded _ NOT CrossLine[player.x, controlGame.scrimmage, team, controlGame.quarter]; IF controlGame.possessor # controlGame.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[controlGame.player[i].x - player.x] > 3 THEN LOOP; IF ABS[controlGame.player[i].y - player.y] > 3 THEN LOOP; RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; passerX: REAL; ballSpeed: REAL _ 1; ballRandom: REAL _ 0.1; grounded: BOOL _ FALSE; RandomDelta: PROC [scale: REAL] RETURNS [x,y: REAL _ 0.0] = { x _ (Random.ChooseInt[rs, 0, 10] - 5) * scale; y _ (Random.ChooseInt[rs, 0, 10] - 5) * scale; }; LiveBall: PROC = { controlGame.state _ liveBall; IF controlGame.ballRange # 0 THEN { x, y: REAL; scale: REAL = 5/controlGame.ballRange; [x,y] _ RandomDelta[scale]; x _ controlGame.player[ball].x + scale*x; y _ controlGame.player[ball].y + scale*y; controlGame.ballRange _ 2*controlGame.ballRange/3; IF controlGame.ballRange < 1 THEN controlGame.ballRange _ 0; controlGame.ballTarget _ controlGame.ballCatchable _ [absolute[x, y]]; }; }; MoveBall: PROC [player: Player, x, y: REAL] = { 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 controlGame.state = pass AND rt < controlGame.ballRange THEN controlGame.state _ catchable; IF grounded AND controlGame.state = catchable THEN FOR i: Position IN Players DO IF TeamOf[i] # controlGame.possessor THEN LOOP; IF ABS[controlGame.player[i].x - controlGame.player[ball].x] > 4 THEN LOOP; IF ABS[controlGame.player[i].y - controlGame.player[ball].y] > 4 THEN LOOP; grounded _ FALSE; ENDLOOP; }; CatchBall: PROC = { delta: REAL _ 10; receiver, ball: Player; closest: Position _ none; IF controlGame.state = pass THEN RETURN; ball _ @controlGame.player[ball]; FOR i: Position IN Players DO receiver _ @controlGame.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 controlGame.state # option AND ~controlGame.pass AND TeamOf[closest] = controlGame.offense THEN {PlayOver[controlGame.player[closest].x, TRUE]; RETURN}; -- grounding a punt controlGame.ballCarrier _ closest; controlGame.possessor _ TeamOf[closest]; IF controlGame.possessor # controlGame.offense THEN controlGame.state _ turnover; }; Catch: PROC [catcher: Player, catchee: Position, delta: REAL _ 1] RETURNS [BOOL] = { x, y: REAL; IF catchee = ball THEN catchee _ controlGame.ballCarrier; IF ABS[catcher.y - controlGame.player[catchee].y] > delta THEN RETURN [FALSE]; IF ABS[catcher.x - controlGame.player[catchee].x] > delta THEN RETURN [FALSE]; IF catchee = ball AND Blocked[catcher] THEN RETURN [FALSE]; IF catchee = ball THEN { [x, y] _ GetTarget[controlGame.ballTarget, none]; IF x = controlGame.player[ball].x AND y = controlGame.player[ball].y THEN RETURN [TRUE]; IF x < catcher.x AND catcher.x < controlGame.player[ball].x THEN RETURN [TRUE]; IF x > catcher.x AND catcher.x > controlGame.player[ball].x THEN RETURN [TRUE]; IF y < catcher.y AND catcher.y < controlGame.player[ball].y THEN RETURN [TRUE]; IF y > catcher.y AND catcher.y > controlGame.player[ball].y THEN RETURN [TRUE]; RETURN [FALSE]}; RETURN [TRUE]; }; PlayOver: PROC [scrimmage: REAL, stopClock: BOOL _ FALSE] = { controlGame.clockStopped _ stopClock; IF controlGame.ballCarrier = none AND controlGame.pass AND ~controlGame.lateral THEN scrimmage _ controlGame.scrimmage; scrimmage _ HandlePenalties[scrimmage]; IF EndZone[scrimmage] THEN Score[scrimmage]; IF controlGame.clock <= 0 THEN { controlGame.clock _ 15*60; controlGame.clockStopped _ TRUE; controlGame.quarter _ controlGame.quarter + 1; IF controlGame.quarter = 3 OR controlGame.quarter = 5 THEN {OffField[]; controlGame.timeouts _ [4, 4]; RETURN}; scrimmage _ 100 - scrimmage; controlGame.scrimmage _ 100 - controlGame.scrimmage; controlGame.firstDownMarker _ 100 - controlGame.firstDownMarker}; IF EndZone[scrimmage] THEN {Wait[60]; KickOff[Opponent[controlGame.possessor]]; RETURN}; controlGame.scrimmage _ scrimmage; IF controlGame.possessor # controlGame.offense THEN { controlGame.offense _ controlGame.possessor; FirstDown[]}; IF CrossLine[scrimmage, controlGame.firstDownMarker, controlGame.offense, controlGame.quarter] THEN FirstDown[]; IF controlGame.down = 5 THEN { controlGame.offense _ Opponent[controlGame.offense]; controlGame.possessor _ controlGame.offense; FirstDown[]}; Wait[60]; Huddle[]; }; HandlePenalties: PROC [scrimmage: REAL] RETURNS [REAL] = { IF grounded AND controlGame.possessor # controlGame.offense THEN grounded _ FALSE; -- interception IF grounded THEN {controlGame.penalties[controlGame.offense] _ grounding; grounded _ FALSE}; IF controlGame.penalties[home] = offSides AND controlGame.penalties[visitors] = offSides THEN {controlGame.penaltyState _ canceled; RETURN [controlGame.scrimmage]}; IF controlGame.penalties[home] # none AND controlGame.penalties[visitors] # none THEN {controlGame.down _ controlGame.down + 1; controlGame.penaltyState _ canceled; RETURN [controlGame.scrimmage]}; FOR team: Team IN Team DO SELECT controlGame.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; controlGame.down _ controlGame.down + 1; RETURN [scrimmage]; }; Accepted: PROC RETURNS [BOOL] = { controlGame.penaltyState _ asking; WHILE controlGame.penaltyState = asking DO Wait[10]; ENDLOOP; RETURN [controlGame.penaltyState = accepted]; }; SetPenalty: PROC [team: Team, yards: REAL] RETURNS [line: REAL] = { controlGame.penaltyState _ accepted; IF Direction[team, controlGame.quarter] = 1 THEN IF controlGame.scrimmage <= yards THEN RETURN [(controlGame.scrimmage+1)/2] ELSE RETURN [controlGame.scrimmage - yards] ELSE IF (100 - controlGame.scrimmage) <= yards THEN RETURN [(100 + controlGame.scrimmage)/2] ELSE RETURN [controlGame.scrimmage + yards]; }; Score: PROC [scrimmage: REAL] = { goal: INTEGER; opponent: Team; controlGame.clockStopped _ TRUE; goal _ Goal[controlGame.possessor, controlGame.quarter]; opponent _ Opponent[controlGame.possessor]; IF controlGame.ballCarrier = none THEN { -- check for field goal IF scrimmage IN [-9..109] THEN RETURN; IF controlGame.player[ball].y NOT IN [fieldWidth/3..2*fieldWidth/3] THEN RETURN}; SELECT TRUE FROM controlGame.ballCarrier = none AND goal > 50 AND scrimmage < 50 => controlGame.score[controlGame.possessor] _ controlGame.score[controlGame.possessor] + 3; controlGame.ballCarrier = none AND goal < 50 AND scrimmage > 50 => controlGame.score[controlGame.possessor] _ controlGame.score[controlGame.possessor] + 3; goal > 50 AND scrimmage < 50 => controlGame.score[controlGame.possessor] _ controlGame.score[controlGame.possessor] + 7; goal < 50 AND scrimmage > 50 => controlGame.score[controlGame.possessor] _ controlGame.score[controlGame.possessor] + 7; goal > 50 AND scrimmage > 50 => controlGame.score[opponent] _ controlGame.score[opponent] + 2; goal < 50 AND scrimmage < 50 => controlGame.score[opponent] _ controlGame.score[opponent] + 2; ENDCASE; }; SetOffField: PROC = { player: Player; command: ARRAY [0..6) OF Command; command _ offField1; FOR i: CARDINAL IN [0..6) DO player _ @controlGame.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 _ @controlGame.player[Add[VQB, i]]; WITH t: command[i].target SELECT FROM absolute => {player.x _ t.x; player.y _ t.y}; ENDCASE => ERROR; ENDLOOP; OffField[]; }; 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]]]]; rs: Random.RandomStream _ Random.Create[]; Init[]; } . . . <FootballControl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Maxwell, March 15, 1983 10:21 am Spreitzer, May 1, 1984 10:58:28 pm PDT Russ Atkinson (RRA) August 14, 1985 4:28:01 pm PDT RPC procedure Control procedures move the ball back so that the center won't catch it check for offSides and illegal formations no check for defense in motion Not really in motion 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 decelerate there is an upper bound on the velocity If one is being blocked then one cannot move as fast We are close enough, but this player only can be blocked if blocker is closer to the ball than we are. Now update the position, allowing for a little tipsiness take blocking into account Dealing with the ball check for penalties run => ballCarrier crossed line of scrimmage throw the ball the ball's target is half of the range after [x,y] (but allow a little error) add in random errors based on the range (more range, more error) Now update the position, allowing for a little tipsiness select a random direction move the ball in that direction has to be moving toward the receiver 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 Κφ– "cedar" style˜codešœ™Kšœ Οmœ1™šžœžœ˜K˜*K˜——šžœžœž˜=Kšœ ˜ —šžœ žœžœ&žœ˜]Kšœ žœ˜Kšœžœ˜&šžœžœž˜Kšœ žœ5žœ˜IKšœ8˜8Kšœ@˜@Kšžœ=˜D—Kšœ žœ˜'—šžœ)žœ-˜ZKšžœžœ!žœ˜I—Kšœ™K˜K˜šžœžœ˜"Kšœžœžœ žœžœžœžœ žœ˜I—šžœžœ ž˜CKšœDžœ ˜PKšžœžœ˜—šžœžœ ž˜CKšœEžœ˜OKšžœžœ˜—Kšœ ™ šžœžœ žœ˜FK˜1K˜8šžœ žœ žœ ˜ešžœžœ˜,Kšžœ!žœ˜*Kšžœ˜#———Kšœ™šžœžœ˜"K˜MKšžœ˜—Kšœ™K˜šžœžœžœž˜Kšœ!žœ˜*šžœžœžœ˜0Kšžœžœžœ˜%K˜7Kšœžœžœ˜ —K˜Kšžœ˜—Kšœ™K˜šžœžœžœž˜Kšœ!žœ˜*šžœžœžœ˜0Kšžœžœžœ˜%K˜7Kšœžœžœ˜ —K˜Kšžœ˜—Kšžœ˜—Kšœ˜K˜—Kšœ žœ˜Kšœžœ˜Kšœžœ˜šœžœ˜K˜—šŸ œžœ,˜=Kšœžœ˜ K˜K˜Kšœ žœ'˜6K˜ K˜ Kšžœžœ˜.Kšœ™šžœžœžœ˜(Kšžœžœžœ˜+Kšžœžœ˜(K˜K˜"Kšžœ˜—Kšœ™šžœ#žœ ˜HKš žœ(žœžœžœžœ˜UK˜8Kšžœ˜—Kšžœžœžœ˜Kšœ"™"šžœžœ˜šžœ žœž˜šœ žœ žœ˜Kšžœžœ9˜RKšžœžœžœ˜!Kšžœžœ ˜D—˜K˜K˜š žœ žœžœžœžœžœ˜LKšžœ'˜+Kšžœ2˜6—Kšžœ žœ'˜9Kšœ žœžœžœ˜Hšžœ žœžœ˜$Kšœžœ˜:——Kšžœ˜——Kšœ™K˜šžœ žœž˜šœ žœ˜Kšžœ3˜7šžœžœ žœ%˜;Kšžœžœ ˜*Kšžœ˜——K˜Kšœ)žœ˜/Kšžœ˜—Kšœ˜——šœ™Kšœ žœ˜Kšœ žœ˜K˜š Ÿœžœžœ žœžœ ˜^Kšœžœ˜Kšœžœ˜Kšœ žœ3˜DKš œžœžœ žœžœ˜/Kšœ žœ˜ Kšœžœ˜'Kšœ™Kšœžœ ˜ Kšžœ žœ ˜0šžœžœžœ žœ ˜)Kšžœ9˜=KšžœL˜P—šžœžœžœ˜$Kšœ ™ K˜K˜K˜—Kšœ(™(K˜=šžœžœ žœ˜Jšœ4™4šžœ žœ ž˜Kšœ*˜*Kšžœ%žœžœ˜1šžœ%žœ˜-šžœ&žœ˜.Kšœf™fKšœ)˜)šžœ9žœ˜AKšœ˜Kšžœ˜K˜—K˜—Kšœ˜—Kšžœ˜—J˜—šžœžœ˜Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜—˜Kšœ8™8Kš œžœžœžœžœ ˜BKšœžœ+˜3Kšœžœ+˜3Kšœ'˜'K˜'K˜—Kšžœžœžœ žœ˜EKšžœžœžœžœ˜Mšžœ+žœ˜3K˜&K˜&K˜(K˜)—šžœž˜#Kšœ™šžœ žœ ž˜Kšžœžœžœ˜!Kšžœžœžœ˜)K˜%Kšžœ˜——Kšœ˜K˜—šŸ œžœžœžœ˜?Kšœžœ˜Kšœžœ˜Kšžœ˜K˜K˜—šŸ œžœ žœ žœ˜JKšœžœžœ žœ˜@Kšœžœ(žœ˜;šžœ žœž˜Kšœžœ"˜1šœ žœžœ žœ˜^šžœžœžœžœ˜4Kšœ žœžœž˜1—šžœ˜Kšœžœ˜Kšœžœ˜K˜K˜%Kšžœžœžœ ˜.Kš žœžœ žœžœ žœžœ ˜@K˜2Kšžœžœžœ ˜#K˜BKšžœ"˜)——š œ žœžœ žœžœ˜OK˜—Kšœ žœ ˜Kšžœžœ˜—Kšœ˜K˜—šŸœžœ˜*Kšœžœ˜Kšžœ%žœ ˜6Kšžœ%žœ ˜6Kšžœžœ žœžœ˜2Kšžœžœ žœžœ˜2K˜6K˜6Kšœ˜K˜—šŸœžœžœžœ˜1šžœ žœ ž˜Kšžœ%žœžœ˜1Kšžœ2žœžœžœ˜GKšžœ˜—Kšžœžœ˜Kšœ˜K˜—š Ÿœžœžœ žœžœ˜^K˜Kšœžœ˜Kšœžœ˜šŸ œžœ˜#Kšœžœžœ˜Kšœžœžœ˜Kšžœ žœžœ˜Kšžœ žœžœ˜šžœ.ž˜3KšœUžœžœ˜a—Kšœžœ˜Kšœžœ˜šžœ˜Kšžœžœ žœ žœ0˜QKšžœžœ žœ žœ2˜S——šžœžœ ˜ Kš žœžœ žœžœ$ž˜RKš žœžœ žœ žœ$žœ˜O—Kšžœžœ˜*Kšœ˜K˜—š Ÿœžœžœžœžœ˜BKšžœžœžœžœ˜+Kšœ˜——šœ™šŸœžœžœžœ˜7K˜ Kšœžœ˜Kšœ™K˜Kš œ žœžœžœžœ˜@Kšžœ žœ˜$Kšœžœ˜šžœžœž˜(Kšœ-™-šžœ3˜5Kšžœ+ ˜FKšžœžœ˜ ——Kšœ™K˜K˜K˜$šžœ žœ˜Kšœžœ ˜Kšœ˜Kšœ˜K˜ K˜—šžœ žœ˜KšœM™MKšœžœ#˜-Kšœžœ ˜Kšœ@™@šžœ ž˜Kšœžœ˜ Kšœ"˜"K˜ K˜ K˜Kšžœ˜—K˜(Kšœ˜Kšœ˜K˜,šžœ˜ Kšžœ/˜3Kšžœ<˜@—K˜2K˜K˜K˜K˜—Kšœ˜K˜—šŸœžœžœ žœ˜NKšœ žœG˜UKšžœ-žœ žœ˜EK˜š žœ žœžœ žœ ž˜.Kšžœ%žœžœ˜1Kšžœžœ)žœžœ˜9Kšžœžœ)žœžœ˜9Kšžœžœ˜Kšžœ˜—Kšžœžœ˜Kšœ˜K˜—Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœžœ˜K˜š Ÿ œžœ žœžœžœ ˜=Kšœ8™8Kšœ.˜.Kšœ.˜.K˜—K˜šŸœžœ˜K˜šžœžœ˜#Kšœžœ˜ Kšœžœ˜&Kšœ™Kšœ˜Kšœ™Kšœ)˜)Kšœ)˜)K˜2Kšžœžœ˜Kšžœžœ žœžœ˜;—Kšžœ˜—K˜.Kšžœ ˜Kšœ˜K˜—šŸœžœžœžœ˜!K˜"Kšžœ#žœ žœ˜=Kšžœ&˜-Kšœ˜K˜—š Ÿ œžœžœžœžœ˜CK˜$šžœ)˜+šžœžœ ˜'Kšžœžœ˜)Kšžœžœ˜+—šžœžœ'˜.Kšžœžœ!˜-Kšžœžœ ˜,——Kšœ˜K˜—šŸœžœ žœ˜!Kšœžœ˜K˜Kšœžœ˜ K˜8K˜+šžœ žœ ˜@Kšžœ žœ žœžœ˜&Kš žœžœžœ žœžœ˜Q—šžœžœž˜šœžœ žœ˜BK˜X—šœžœ žœ˜BK˜X—šœ žœ˜!K˜X—šœ žœ˜ K˜X—šœ žœ˜ K˜>—šœ žœ˜!K˜>—Kšžœ˜—Kšœ˜——šœ ™ šŸ œžœ˜K˜Kšœ žœžœ ˜!K˜šžœžœžœž˜Kšœ!žœ˜*šžœžœž˜%K˜-Kšžœžœ˜—Kšžœ˜—K˜šžœžœžœž˜Kšœ!žœ˜*šžœžœž˜%K˜-Kšžœžœ˜—Kšžœ˜—K˜ Kšœ˜K˜—˜K˜QK˜QK˜—˜K˜QK˜QK˜—K˜*K˜K˜K˜—Kšœ˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜—…—`Ϊ