<> <> <> <> <> 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 < ballCarrier crossed line of scrimmage >> 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; <