DIRECTORY Basics USING [LowHalf], BasicTime USING [GMT, Now, nullGMT, Pulses, GetClockPulses], FootballInternal, FootballMaster, FootballMasterRpcControl USING [ExportInterface, UnexportInterface], Process USING [SetTimeout], RealFns USING [SqRt], Rope USING [ROPE], RPC USING [MakeKey], UserCredentials USING [Get]; FootballControl: MONITOR IMPORTS Basics, BasicTime, FootballMaster, FootballMasterRpcControl, Process, 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 => { }; player.dx > -0.5 AND player.dx < 0.5 AND player.dy > -0.5 AND player.dy < 0.5 => { }; 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; Move: PROC[player: Player, x, y: REAL, stopping: BOOL _ FALSE, receiver: Position _ none] = { ax: REAL _ x - player.x; ay: REAL _ y - player.y; speed: REAL _ IF TeamOf[player.position] # controlGame.possessor THEN .22 ELSE .2; 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 rt > speedProd THEN { temp: REAL = speedProd/rt; player.dx _ player.dx*temp; player.dy _ player.dy*temp; }; ax _ player.x + player.dx/inertia; ay _ player.y + player.dy/inertia; 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; }; 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, dy: REAL; IF ABS[other.x - x] > delta THEN RETURN; IF ABS[other.y - y] > 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 _ 1.0; grounded: BOOL _ FALSE; RandomDelta: PROC [scale: REAL] RETURNS [x,y: REAL _ 0.0] = { time: BasicTime.Pulses = BasicTime.GetClockPulses[]; low: CARDINAL = Basics.LowHalf[time]; SELECT low MOD 3 FROM 1 => x _ scale; 2 => x _ -scale; ENDCASE; SELECT (low / 3) MOD 3 FROM 1 => y _ scale; 2 => y _ -scale; ENDCASE; }; 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 { -- has to be moving toward the receiver [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]]]]; 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) January 23, 1985 9:20:39 pm PST ****************************************************************** 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 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) 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 ****************************************************************** ΚW– "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šœ˜K˜—K™KšœB™BKšœ ™ KšœB™BK˜šŸ œžœ˜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˜—…—^„!