DIRECTORY BasicTime, Commander, CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, Process, Random, Rope, RPC, ThisMachine, TIPUser, UserCredentials, UserProfile, ViewerClasses, ViewerLocks, ViewerOps; MazeWarPlayerImpl1: CEDAR MONITOR IMPORTS BasicTime, CommandTool, GenSym, Icons, InputFocus, IO, Labels, MazeWarPlayerInsides, MazeWarPlayerRpcControl, MessageWindow, Process, Random, Rope, RPC, ThisMachine, TIPUser, UserCredentials, UserProfile, ViewerLocks, ViewerOps EXPORTS MazeWarFinder, MazeWarPlayer, MazeWarPlayerInsides = BEGIN OPEN MazeWarPlayerInsides; mazeViewerFlavor: PUBLIC ATOM _ GenSym.Gen[]; mazeViewerClass: PUBLIC ViewerClasses.ViewerClass _ NEW [ViewerClasses.ViewerClassRec _ [ paint: PaintMaze, notify: NotifyMaze, destroy: DestroyMaze, tipTable: --TIPUser.InstantiateNewTIPTable[UserProfile.Token[key: "MazeWar.TIPTable", default: "/Ivy/Spreitzer/Games/MazeWar.TIP"]]-- NIL ]]; installationDirectory: ROPE _ CommandTool.CurrentWorkingDirectory[]; mazeWarriorIcon: PUBLIC Icons.IconFlavor _ Icons.NewIconFromFile[file: "MazeWar.icons", n: 0]; currentTIPTableName: ROPE _ " "; rs: Random.RandomStream _ Random.Create[seed: -1]; quiverTime: PUBLIC BasicTime.Pulses _ BasicTime.MicrosecondsToPulses[2000000]; CallUnderMonitor1: PUBLIC ENTRY PROC [p: PROC] = BEGIN ENABLE UNWIND => {}; p[]; END; StartPlayer: PUBLIC PROC [cmd: Commander.Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = BEGIN MonitoredStart: ENTRY PROC = BEGIN ENABLE UNWIND => {}; --not strictly right, but it helps debugging name, machineName, userPicsSource, normdPicsSource, TIPTableName, mazeSource: ROPE _ NIL; reread: BOOLEAN _ FALSE; cls: IO.STREAM _ IO.RIS[cmd.commandLine]; self: Player _ myself; position: INTEGER; IF self # NIL THEN {cmd.err.PutRope[IF self.destroyed THEN "Still dying... wait 1.5 - 4.5 minutes and try again.\n" ELSE "Already playing.\n"]; RETURN}; paints _ 0; FOR position _ 1, position + 1 DO token: ROPE; GetArg: PROC RETURNS [arg: ROPE] = { arg _ cls.GetTokenRope[TokenBreak].token; IF arg.Equal["="] THEN arg _ cls.GetTokenRope[TokenBreak].token ELSE cmd.err.PutRope["Missing \"=\""]}; ToBool: PROC [r: ROPE] RETURNS [b: BOOL] = { s: IO.STREAM _ IO.RIS[r]; b _ s.GetBool[]; s.Close[]}; [] _ cls.SkipWhitespace[flushComments: FALSE]; IF cls.EndOf[] THEN EXIT; token _ cls.GetTokenRope[TokenBreak].token; IF token.Equal["="] THEN {cmd.err.PutRope["Syntax error"]; RETURN}; IF token.Equal["name", FALSE] THEN name _ GetArg[] ELSE IF token.Equal["pics", FALSE] THEN userPicsSource _ GetArg[] ELSE IF token.Equal["TIPTable", FALSE] THEN TIPTableName _ GetArg[] ELSE IF token.Equal["MazeSource", FALSE] THEN mazeSource _ GetArg[] ELSE IF token.Equal["reread", FALSE] THEN reread _ ToBool[GetArg[]] ELSE SELECT position FROM 1 => name _ token; 2 => userPicsSource _ token; 3 => TIPTableName _ token; 4 => mazeSource _ token; 5 => reread _ ToBool[token]; ENDCASE => { cmd.err.PutF["I won't take a %th argument\n", IO.int[position]]; RETURN}; ENDLOOP; machineName _ ThisMachine.Name[$Pup]; IF playerNumber # 1 THEN machineName _ IO.PutFR["%g#%g", IO.rope[machineName], IO.card[playerNumber]]; IF userPicsSource.Length[] < 1 THEN userPicsSource _ UserProfile.Token[key: "MazeWar.Pics", default: installationDirectory.Cat["Eyeball"]]; IF (normdPicsSource _ NormalizePlayerPicsRoot[userPicsSource]) = NIL THEN { result _ $Failure; msg _ IO.PutFR["Pics %g don't exist or are local with no attachment", IO.rope[userPicsSource]]; RETURN; }; IF name.Length[] < 1 THEN name _ UserProfile.Token[key: "MazeWar.Name", default: NIL]; IF name.Length[] < 1 THEN name _ UserCredentials.Get[].name.Cat["@", machineName]; IF TIPTableName.Length[] < 1 THEN TIPTableName _ UserProfile.Token[key: "MazeWar.TIPTable", default: installationDirectory.Cat["MazeWar.TIP"]]; IF mazeSource.Length[] < 1 THEN mazeSource _ UserProfile.Token[key: "MazeWar.MazeSource", default: installationDirectory.Cat["MazeWar.Maze"]]; IF reread OR NOT TIPTableName.Equal[currentTIPTableName] THEN mazeViewerClass.tipTable _ TIPUser.InstantiateNewTIPTable[currentTIPTableName _ TIPTableName]; IF reread THEN LosePlayerPics[normdPicsSource]; cmd.err.PutRope[MakeNewPlayer[name: name, instance: myInstance, picsSource: normdPicsSource, mazeSource: mazeSource]]; END; MonitoredStart[]; IF myself # NIL THEN ViewerOps.OpenIcon[icon: myself.mv.container, closeOthers: TRUE]; END; TokenBreak: IO.BreakProc = {RETURN [SELECT char FROM IO.SP, IO.CR, IO.ESC, IO.LF, IO.TAB => sepr, '= => break, ENDCASE => other]}; StopPlayer: PUBLIC Commander.CommandProc--PROC [cmd: Handle]-- = BEGIN self: Player _ myself; IF self = NIL THEN {cmd.err.PutRope["Not playing!"]; RETURN}; IF self.destroyed THEN {cmd.err.PutRope["I'm busy dying... should be done in half a minute"]; RETURN}; ViewerOps.DestroyViewer[self.mv.container]; END; UnMakePlayer: ENTRY PROC [p: Player] = BEGIN ENABLE UNWIND => {}; --not strictly right, but it helps debugging IF p.destroyed THEN RETURN; FOR pl: PlayerList _ p.mv.players, pl.rest WHILE pl # NIL DO IF pl.first = myself THEN LOOP; TRUSTED {pl.first.ir.RemovePlayer[p.id !RPC.CallFailed => CONTINUE]}; pl.first.ir _ NIL; --unimports dynamic interface ENDLOOP; IF exportedPlayer THEN MazeWarPlayerRpcControl.UnexportInterface[]; exportedPlayer _ FALSE; p.destroyed _ TRUE; END; paints: PUBLIC INTEGER _ 0; clippable: PUBLIC BOOLEAN _ FALSE; blt: PUBLIC BOOLEAN _ TRUE; Peek: PUBLIC PROC [peek, row, col, angle: INTEGER] RETURNS [prow, pcol, pdrow, pdcol, pangle: INTEGER] = BEGIN [pdrow, pdcol] _ aToD[angle]; IF peek = 0 THEN RETURN [row, col, pdrow, pdcol, angle]; prow _ row + pdrow; pcol _ col + pdcol; [pdrow, pdcol] _ aToD[pangle _ (angle + peek) MOD 4]; END; Visible: PUBLIC PROC [mv: MazeView, frow, fcol, fdrow, fdcol, trow, tcol: INTEGER] RETURNS [distance: INTEGER] = BEGIN drow, dcol: INTEGER; drow _ trow - frow; dcol _ tcol - fcol; IF SGN[drow] # fdrow THEN RETURN [-1]; IF SGN[dcol] # fdcol THEN RETURN [-1]; distance _ 0; DO IF frow = trow AND fcol = tcol THEN RETURN; IF NOT mv.squares[Index[mv, frow, fcol]].open THEN RETURN [-1]; frow _ frow + fdrow; fcol _ fcol + fdcol; distance _ distance + 1; ENDLOOP; END; SGN: PROC [x: INTEGER] RETURNS [sgn: INTEGER] = {sgn _ IF x < 0 THEN -1 ELSE IF x > 0 THEN 1 ELSE 0}; forkNotify: PUBLIC BOOLEAN _ FALSE; NotifyMaze: ViewerClasses.NotifyProc--PROC [self: Viewer, input: LIST OF REF ANY]-- = BEGIN IF NOT forkNotify THEN ReallyNotifyMaze[self, input] ELSE TRUSTED {Process.Detach[FORK ReallyNotifyMaze[self, input]]}; END; ReallyNotifyMaze: ViewerClasses.NotifyProc--PROC [self: Viewer, input: LIST OF REF ANY]-- = BEGIN p: Player _ NARROW[self.data]; IF p = NIL THEN RETURN; SELECT input.first FROM $Grab => {InputFocus.SetInputFocus[self]; RETURN}; $MoveFwd => {MoveSelf[p, p.angle]; RETURN}; $MoveBack => {MoveSelf[p, (p.angle+2) MOD 4]; RETURN}; $Shoot => TRUSTED {Process.Detach[FORK RawShoot[p]]; RETURN}; $TurnLeft => SetDirection[p, (p.angle+3) MOD 4]; $TurnRight => SetDirection[p, (p.angle+1) MOD 4]; $TurnBack => SetDirection[p, (p.angle+2) MOD 4]; $PeekLeft => SetPeek[p, peekLeft]; $PeekRight => SetPeek[p, peekRight]; $UnPeek => SetPeek[p, 0]; $Auto => IF ((auto _ auto.SUCC) MOD 2) # 0 THEN TRUSTED {Process.Detach[FORK Auto[p, auto]]}; $Aggressive => MessageWindow.Append[ message: IF autoParms.aggressive _ NOT autoParms.aggressive THEN "Getting aggressive" ELSE "Becoming mediocre", clearFirst: TRUE]; ENDCASE => ERROR; IncrPaint[p]; END; DestroyMaze: ViewerClasses.DestroyProc--PROC [self: Viewer]-- = BEGIN p: Player _ NARROW[self.data]; IF p = myself THEN UnMakePlayer[p]; END; auto: INT _ 0; autoPeriod: INT; atuoParmsRep: TYPE ~ RECORD [ forwardRange: INT _ 2, forwardChooseAhead: INT _ 0, forwardChooseLeft: INT _ 1, forwardChooseRight: INT _ 2, backRange: INT _ 1, backChooseLeft: INT _ 0, backChooseRight: INT _ 1, lookBehindRange: INT _ 3, lookBehindChoose: INT _ 0, aggressive: BOOL _ FALSE ]; autoParms: REF atuoParmsRep ~ NEW [atuoParmsRep]; Auto: PROC [p: Player, myAutoId: INT] = BEGIN defaultAutoPeriod: INT ~ 400; minAutoPeriod: INT ~ 50; maxAutoPeriod: INT ~ 1000; myLastScore: INT _ p.score; TRUSTED {Process.SetPriority[Process.priorityNormal]}; autoPeriod _ defaultAutoPeriod; DO Do: PROC [action: ATOM] ~ { IF p.destroyed OR p.mv.container.destroyed OR auto # myAutoId THEN ERROR ABORTED; p.mv.maze.class.notify[p.mv.maze, LIST[action]]; Process.Pause[Process.MsecToTicks[autoPeriod]]; }; ahead: Angle ~ 0; left: Angle ~ 3; right: Angle ~ 1; behind: Angle ~ 2; Compose: PROC [d1, d2: Angle] RETURNS [Angle] ~ { RETURN [(d1+d2) MOD 4] }; IsSpace: PROC [dirn: Angle _ ahead, whenPeeking: BOOLEAN _ FALSE] RETURNS [BOOLEAN] ~ { drow, dcol: INT; [drow, dcol] _ aToD[(p.angle+dirn) MOD 4]; IF whenPeeking THEN { fdrow, fdcol: INT; [fdrow, fdcol] _ aToD[p.angle]; drow _ drow+fdrow; dcol _ dcol+fdcol }; RETURN [p.mv.squares[Index[p.mv, p.row, p.col]+Direction[p.mv, drow, dcol]].open]; }; TargetPresent: PROC RETURNS [BOOLEAN] ~ { FOR pl: PlayerList _ p.mv.players, pl.rest WHILE pl # NIL DO IF pl.first.distance >= 0 THEN RETURN [TRUE] ENDLOOP; RETURN [FALSE] }; IF myLastScore # p.score THEN { better: INT _ 0; worse: INT _ 0; FOR pl: PlayerList _ p.mv.players, pl.rest WHILE pl # NIL DO IF pl.first.score > p.score THEN better _ better.SUCC; IF pl.first.score < p.score THEN worse _ worse.SUCC; ENDLOOP; IF better # worse OR autoParms.aggressive THEN { SELECT TRUE FROM (myLastScore > p.score) AND (better > (IF NOT autoParms.aggressive THEN worse ELSE 0)) => autoPeriod _ MAX[(autoPeriod*4)/5, minAutoPeriod]; (myLastScore < p.score) AND (better < (IF NOT autoParms.aggressive THEN worse ELSE 1)) => autoPeriod _ MIN[(autoPeriod*5)/4, maxAutoPeriod]; ENDCASE => NULL; }; myLastScore _ p.score }; SELECT TRUE FROM ~IsSpace[] => { turn: Angle _ SELECT rs.ChooseInt[0, autoParms.backRange] FROM <= autoParms.backChooseLeft => left, <= autoParms.backChooseRight => right, ENDCASE => behind; inc: Angle ~ SELECT turn FROM left => right, right => left, ENDCASE => (SELECT rs.ChooseInt[0, 1] FROM 0=> right, 1=> left, ENDCASE => ERROR); WHILE ~IsSpace[turn] DO turn _ Compose[turn, inc] ENDLOOP; Do[SELECT turn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => $TurnBack]; IF turn = left OR turn = right THEN Do[$MoveFwd] }; TargetPresent[] => { Do[$Shoot]; DO side: Angle _ SELECT rs.ChooseInt[0, 1] FROM 0=> left, 1=> right, ENDCASE => ERROR; IF IsSpace[Compose[ side, behind]] THEN side _ Compose[ side, behind]; IF IsSpace[side] THEN { Do[SELECT side FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR]; Do[$MoveFwd]; EXIT }; IF ~IsSpace[] THEN EXIT; Do[$MoveFwd] ENDLOOP }; ENDCASE => { PeekAndShoot: PROC [dirn: Angle] RETURNS [didShoot: BOOLEAN] ~ { IF ~IsSpace[ dirn, TRUE] THEN RETURN [didShoot _ FALSE]; Do[SELECT dirn FROM left => $PeekLeft, right => $PeekRight, ENDCASE => ERROR]; didShoot _ TargetPresent[]; Do[$UnPeek]; IF didShoot THEN { Do[$MoveFwd]; Do[SELECT dirn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR]; Do[$Shoot]; Do[SELECT dirn FROM left => $TurnLeft, right => $TurnRight, ENDCASE => ERROR]; Do[$MoveFwd]; } }; peekDirn: Angle; IF rs.ChooseInt[0, autoParms.lookBehindRange] <= autoParms.lookBehindChoose AND IsSpace[behind] THEN { Do[$TurnBack]; IF TargetPresent[] THEN LOOP; Do[$TurnBack] }; peekDirn _ SELECT rs.ChooseInt[0, 1] FROM 0=> left, 1=> right, ENDCASE => ERROR; IF ~PeekAndShoot[peekDirn] THEN IF ~PeekAndShoot[Compose[peekDirn, behind]] THEN { turn: Angle _ SELECT rs.ChooseInt[0, autoParms.forwardRange] FROM <= autoParms.forwardChooseAhead => ahead, <= autoParms.forwardChooseLeft => left, <= autoParms.forwardChooseRight => right, ENDCASE => behind; inc: Angle ~ SELECT turn FROM left => right, right => left, ENDCASE => (SELECT rs.ChooseInt[0, 1] FROM 0=> right, 1=> left, ENDCASE => ERROR); WHILE ~IsSpace[turn] DO turn _ Compose[turn, inc] ENDLOOP; Do[SELECT turn FROM ahead => $MoveFwd, left => $TurnLeft, right => $TurnRight, ENDCASE => $TurnBack]; IF turn # ahead THEN Do[$MoveFwd] } }; ENDLOOP; END; ListPlayers: PUBLIC UNSAFE PROC RETURNS [players: BasicPlayerList] = BEGIN IF myself = NIL THEN RETURN [NIL]; FOR pl: PlayerList _ myself.mv.players, pl.rest WHILE pl # NIL DO players _ CONS[[pl.first.instanceName, pl.first.id], players]; ENDLOOP; FOR pl: PlayerList _ myself.mv.differentMazePlayers, pl.rest WHILE pl # NIL DO players _ CONS[[pl.first.instanceName, pl.first.id], players]; ENDLOOP; END; TakePlayer: PUBLIC UNSAFE PROC [bp: BasicPlayer] = UNCHECKED {KnowPlayer[bp]}; GetFinderID: PUBLIC UNSAFE PROC RETURNS [bp: BasicPlayer] = {self: Player _ myself; bp _ IF self # NIL THEN [self.instanceName, self.id] ELSE [myInstance, 0]}; GetState: PUBLIC UNSAFE PROC RETURNS [id: PlayerId, name, pics, maze: ROPE, row, col, score: INTEGER, angle: Angle] = BEGIN RETURN [myself.id, myself.name, myself.pics.name, myself.mv.asRope, myself.row, myself.col, myself.score, myself.angle]; END; KnowPlayer: PUBLIC UNSAFE PROC [bp: BasicPlayer] = BEGIN q: Player; IF myself = NIL THEN RETURN; q _ EnsurePlayer[bp]; IF q # NIL AND NOT q.instanceName.Equal[bp.instance] THEN MessageWindow.Append["Random number collision detected", TRUE]; END; EnsurePlayer: PUBLIC PROC [bp: BasicPlayer] RETURNS [q: Player] = BEGIN ir: PlayerInterface _ NIL; alreadyKnown, differentMaze: BOOLEAN; id: PlayerId; row, col, score: INTEGER; angle: Angle; name, picsSource, mazeRope: ROPE; pp: PlayerPics; Internals1: PROC = BEGIN ENABLE UNWIND => NULL; IF alreadyKnown _ (q _ FindPlayer[myself.mv.players, bp.id]) # NIL THEN RETURN; IF alreadyKnown _ (q _ FindPlayer[myself.mv.differentMazePlayers, bp.id]) # NIL THEN RETURN; ir _ MazeWarPlayerRpcControl.ImportNewInterface[interfaceName: [instance: bp.instance] !RPC.ImportFailed => {ir _ NIL; CONTINUE}]; IF ir = NIL THEN {ok _ FALSE; RETURN}; TRUSTED {[id, name, picsSource, mazeRope, row, col, score, angle] _ ir.GetState[ !RPC.CallFailed => {ok _ FALSE; CONTINUE}]}; IF NOT ok THEN RETURN; IF id # bp.id THEN {ok _ FALSE; RETURN}; differentMaze _ NOT mazeRope.Equal[myself.mv.asRope, FALSE]; END; Internals2: ENTRY PROC = BEGIN ENABLE UNWIND => NULL; IF alreadyKnown _ (q _ FindPlayer[myself.mv.players, bp.id]) # NIL THEN RETURN; IF alreadyKnown _ (q _ FindPlayer[myself.mv.differentMazePlayers, bp.id]) # NIL THEN RETURN; IF differentMaze THEN BEGIN myself.mv.differentMazePlayers _ CONS[NEW [PlayerRep _ [id: bp.id, instanceName: bp.instance, quiverTill: BasicTime.GetClockPulses[]-1]], myself.mv.differentMazePlayers]; END ELSE BEGIN q _ AddPlayerAt[myself.mv, ir, bp.instance, id, name, row, col, angle, score, pp]; AddScoring[myself, q]; END; END; ok: BOOLEAN _ TRUE; me: Player _ myself; --'cause the debugger can't. q _ NIL; Internals1[]; IF NOT ok THEN RETURN [NIL]; IF q # NIL THEN RETURN; IF NOT differentMaze THEN pp _ GetPlayerPics[picsSource]; ViewerLocks.CallUnderWriteLock[Internals2, myself.mv.container]; IF NOT ok THEN RETURN [NIL]; IF alreadyKnown THEN RETURN; IF differentMaze THEN BEGIN --nothing to do here!?-- END ELSE BEGIN ViewerOps.PaintViewer[viewer: myself.mv.container, hint: client]; TRUSTED {ir.KnowPlayer[[myself.instanceName, myself.id] !RPC.CallFailed => {ok _ HandleCallFailure[q, why]; CONTINUE}]}; NoticeCall[q, ok]; END; END; TakeStatus: PUBLIC UNSAFE PROC [id: PlayerId, row, col, score: INTEGER, angle: Angle, shot: BOOLEAN] = BEGIN q: Player; IF (q _ FindPlayer[myself.mv.players, id]) = NIL THEN RETURN; IF row # q.row OR col # q.col OR angle # q.angle THEN { q.shot _ shot; MoveOther[myself, q, row, col, angle]}; IF score # q.score THEN Labels.Set[q.scoreLabel, IO.PutFR["%6g", IO.int[q.score _ score]]]; END; RemovePlayer: PUBLIC UNSAFE PROC [id: PlayerId] = BEGIN MonitoredRemove: ENTRY PROC = BEGIN goner: Player; index: INTEGER; goner _ FindPlayer[myself.mv.players, id]; IF goner = NIL THEN RETURN; myself.mv.players _ Filter[myself.mv.players, goner]; ViewerOps.DestroyViewer[goner.nameLabel, FALSE]; ViewerOps.DestroyViewer[goner.scoreLabel, FALSE]; index _ Index[myself.mv, goner.row, goner.col]; myself.mv.squares[index].occupants _ Filter[myself.mv.squares[index].occupants, goner]; goner.ir _ NIL; --Unimports dynamic interface goner.mv _ NIL; END; IF myself = NIL THEN RETURN; ViewerLocks.CallUnderWriteLock[MonitoredRemove, myself.mv.container]; ViewerOps.PaintViewer[myself.mv.container, client]; END; FindPlayer: PUBLIC PROC [pl: PlayerList, id: PlayerId] RETURNS [p: Player] = BEGIN FOR pl _ pl, pl.rest WHILE pl # NIL DO IF pl.first.id = id THEN RETURN [pl.first]; ENDLOOP; p _ NIL; END; MoveSelf: PROC [p: Player, angle: Angle] = BEGIN moving: BOOLEAN; MonitoredMove: ENTRY PROC = BEGIN di, me, nu, drow, dcol: INTEGER; [drow, dcol] _ aToD[angle]; di _ Direction[p.mv, drow, dcol]; me _ Index[p.mv, p.row, p.col]; nu _ me+di; IF moving _ p.mv.squares[nu].open THEN moving _ MoveSelfBegin[p, p.row+drow, p.col+dcol, p.angle]; END; ViewerLocks.CallUnderWriteLock[MonitoredMove, myself.mv.container]; IF moving THEN MoveSelfEnd[p, FALSE]; END; MoveSelfWork: PROC [p: Player, row, col: INTEGER, angle: Angle, shot: BOOLEAN] = BEGIN changed: BOOL; EntryWork: ENTRY PROC = { p.shot _ shot; changed _ MoveSelfBegin[p, row, col, angle]}; ViewerLocks.CallUnderWriteLock[EntryWork, myself.mv.container]; IF changed THEN MoveSelfEnd[p, shot]; END; MoveSelfBegin: INTERNAL PROC [p: Player, row, col: INTEGER, angle: Angle] RETURNS [changed: BOOLEAN] = BEGIN old, new: INTEGER; IF row # p.row OR col # p.col THEN { old _ Index[p.mv, p.row, p.col]; new _ Index[p.mv, row, col]; p.mv.squares[old].occupants _ Filter[p.mv.squares[old].occupants, p]; p.mv.squares[new].occupants _ CONS[p, p.mv.squares[new].occupants]} ELSE IF angle = p.angle THEN RETURN [FALSE]; changed _ TRUE; p.row _ row; p.col _ col; p.angle _ angle; RecomputeVisibilities[p]; END; MoveSelfEnd: PROC [p: Player, shot: BOOLEAN] = BEGIN AnnounceStatus[shot]; IF shot THEN TRUSTED {Process.SetPriority[Process.priorityNormal]}; IncrPaint[p]; END; RecomputeVisibilities: PROC [from: Player] = BEGIN now: BasicTime.Pulses = BasicTime.GetClockPulses[]; row, col, drow, dcol: INTEGER; [row, col, drow, dcol, ] _ Peek[from.peek, from.row, from.col, from.angle]; FOR pl: PlayerList _ from.mv.players, pl.rest WHILE pl # NIL DO quiver: BOOL = pl.first.quiverTill - now <= quiverTime; wasVisible: BOOLEAN _ pl.first.distance > 0; nowVisible: BOOLEAN; IF pl.first = from THEN LOOP; nowVisible _ (pl.first.distance _ Visible[from.mv, row, col, drow, dcol, pl.first.row, pl.first.col]) > 0; IF wasVisible # nowVisible OR quiver # pl.first.quivering THEN { Labels.SetDisplayStyle[pl.first.nameLabel, IF quiver THEN $BlackOnGrey ELSE IF nowVisible THEN $WhiteOnBlack ELSE $BlackOnWhite]; pl.first.quivering _ quiver; }; ENDLOOP; END; MoveOther: PROC [p, q: Player, row, col: INTEGER, angle: Angle] = BEGIN EntryWork: ENTRY PROC = BEGIN now: BasicTime.Pulses = BasicTime.GetClockPulses[]; quiver: BOOL = q.quiverTill - now <= quiverTime; index: INTEGER _ Index[q.mv, q.row, q.col]; wasShown _ q.sdistance > 0; wasVisible _ q.distance > 0; q.mv.squares[index].occupants _ Filter[q.mv.squares[index].occupants, q]; q.row _ row; q.col _ col; q.angle _ angle; index _ Index[q.mv, q.row, q.col]; q.mv.squares[index].occupants _ CONS[q, q.mv.squares[index].occupants]; nowVisible _ (q.distance _ Visibility[p, q]) > 0; IF wasVisible # nowVisible OR quiver # q.quivering THEN BEGIN Labels.SetDisplayStyle[label: q.nameLabel, style: IF quiver THEN $BlackOnGrey ELSE IF nowVisible THEN $WhiteOnBlack ELSE $BlackOnWhite]; q.quivering _ quiver; END; END; wasShown, wasVisible, nowVisible: BOOLEAN; ViewerLocks.CallUnderWriteLock[EntryWork, myself.mv.container]; IF wasShown OR nowVisible THEN IncrPaint[q]; END; Visibility: PUBLIC PROC [from, to: Player] RETURNS [distance: INTEGER] = BEGIN frow, fcol, drow, dcol: INTEGER; [frow, fcol, drow, dcol, ] _ Peek[from.peek, from.row, from.col, from.angle]; distance _ Visible[from.mv, frow, fcol, drow, dcol, to.row, to.col]; END; ShownDistance: PROC [from, to: Player] RETURNS [distance: INTEGER] = BEGIN frow, fcol, drow, dcol: INTEGER; [frow, fcol, drow, dcol, ] _ Peek[from.speek, from.srow, from.scol, from.sangle]; distance _ Visible[from.mv, frow, fcol, drow, dcol, to.srow, to.scol]; END; SetDirection: PROC [p: Player, angle: Angle] = {MonitoredSet: ENTRY PROC = {RecomputeVisibilities[p]}; p.angle _ angle; ViewerLocks.CallUnderWriteLock[MonitoredSet, myself.mv.container]; AnnounceStatus[FALSE]; }; SetPeek: PROC [p: Player, peek: INTEGER] = {ReallySetPeek: ENTRY PROC = { p.peek _ peek; RecomputeVisibilities[p]; }; ViewerLocks.CallUnderWriteLock[ReallySetPeek, myself.mv.container]; }; shootDelay: Process.Ticks _ Process.MsecToTicks[700]; ProcessList: TYPE = LIST OF PROCESS; RawShoot: PROC [p: Player] = BEGIN row, col, drow, dcol: INTEGER; victims: PlayerList _ NIL; processes: ProcessList _ NIL; EntryShoot: ENTRY PROC = BEGIN ENABLE UNWIND => NULL; row _ p.row; col _ p.col; [drow, dcol] _ aToD[p.angle]; FOR pl: PlayerList _ p.mv.players, pl.rest WHILE pl # NIL DO IF pl.first = p THEN LOOP; IF pl.first.distance > 0 THEN victims _ CONS[pl.first, victims]; ENDLOOP; END; EntryShoot[]; IF victims = NIL THEN RETURN; Process.Pause[shootDelay]; FOR pl: PlayerList _ victims.rest, pl.rest WHILE pl # NIL DO processes _ CONS[FORK FinishShoot[p, pl.first, row, col, drow, dcol], processes]; ENDLOOP; FinishShoot[p, victims.first, row, col, drow, dcol]; FOR processes _ processes, processes.rest WHILE processes # NIL DO TRUSTED {[] _ JOIN processes.first}; ENDLOOP; Labels.Set[p.scoreLabel, IO.PutFR["%6g", IO.int[p.score]]]; AnnounceStatus[FALSE]; END; FinishShoot: PROC [p, q: Player, row, col, drow, dcol: INTEGER] = { ok: BOOLEAN _ TRUE; shot: BOOLEAN _ FALSE; TRUSTED {shot _ q.ir.Shoot[p.id, row, col, drow, dcol !RPC.CallFailed => {ok _ HandleCallFailure[q, why]; CONTINUE}]}; NoticeCall[q, ok]; IF ok AND shot THEN BEGIN Quiver: ENTRY PROC = { ENABLE UNWIND => NULL; q.quiverTill _ BasicTime.GetClockPulses[] + quiverTime; IF NOT q.quivering THEN { q.quivering _ TRUE; Labels.SetDisplayStyle[q.nameLabel, $BlackOnGrey]; }; p.score _ p.score + 10; }; ViewerLocks.CallUnderWriteLock[Quiver, myself.mv.container]; END; }; Shoot: PUBLIC UNSAFE PROC [shooterId: PlayerId, row, col, drow, dcol: INTEGER] RETURNS [shot: BOOLEAN] = BEGIN shooter: Player _ FindPlayer[myself.mv.players, shooterId]; IF shooter = NIL THEN RETURN; DO row _ row + drow; col _ col + dcol; IF NOT myself.mv.squares[Index[myself.mv, row, col]].open THEN EXIT; IF row = myself.row AND col = myself.col THEN {HitPlayer[victor: shooter, victim: myself]; RETURN [TRUE]}; ENDLOOP; shot _ FALSE; END; HitPlayer: PROC [victor, victim: Player] = BEGIN row, col: INTEGER; angle: Angle; ok: BOOLEAN _ TRUE; IF victim # myself THEN ERROR; [row, col] _ PickAPlace[victim.mv]; angle _ rs.ChooseInt[0, 3]; Labels.Set[victim.scoreLabel, IO.PutFR["%6g", IO.int[victim.score _ victim.score - 5]]]; MoveSelfWork[victim, row, col, angle, TRUE]; END; Filter: PUBLIC PROC [org: PlayerList, rem: Player] RETURNS [ohne: PlayerList] = BEGIN last: PlayerList _ NIL; ohne _ org; FOR org _ org, org.rest WHILE org # NIL DO IF org.first = rem THEN {IF last = NIL THEN ohne _ org.rest ELSE last.rest _ org.rest; RETURN}; last _ org; ENDLOOP; ERROR; END; HandleCallFailure: PUBLIC PROC [callee: Player, why: RPC.CallFailure] RETURNS [handled: BOOLEAN _ FALSE] = BEGIN IF why = unbound THEN BEGIN TRUSTED {RemovePlayer[callee.id]}; handled _ TRUE; END; IF debugFailures THEN whys _ CONS[why, whys]; END; debugFailures: BOOLEAN _ FALSE; whys: LIST OF RPC.CallFailure _ NIL; maxAcceptableCallFailures: NAT _ 0; NoticeCall: PUBLIC PROC [p: Player, ok: BOOLEAN] = BEGIN IF p.destroyed THEN RETURN; p.callFailures _ MAX[0, (p.callFailures + (IF ok THEN -1 ELSE 1))]; IF p.callFailures > maxAcceptableCallFailures THEN TRUSTED {RemovePlayer[p.id]}; END; StupidFuckingWarnings: PUBLIC SIGNAL [atom: ATOM] = CODE; END. †[Cedar]Top>MazeWar.df=>MazeWarPlayerImpl1.Mesa Mike Spreitzer July 19, 1986 1:44:10 pm PDT Hal Murray, March 16, 1986 7:23:12 pm PST INVARIANT p.srow, p.scol, p.sangle, and p.speek describe: If p is me: the topview and the hallview, Otherwise: no guarantees. p.distance > 0 <=> p appears in hallview. p.srow, p.scol, and p.sangle describe p's appearance. The "occupants" fields of the Squares are redundant with the p.rows and p.cols. p.distance is redundant with current positions. Video inversness of opponent names is redundant with current positions. All Players are "fully formed". No more than one user being served. myself # NIL <=> exactly one user being served. THROUGH [1 .. playerNumber] DO instance _ instance.Cat["1"] ENDLOOP; IF p.pausing AND input.first # $Grab THEN { MessageWindow.Append[" patience!", FALSE]; RETURN}; I'm not the middle player. SpeedUp. SlowDown. Κπ– "cedar" style˜šœΟmœ(™;Icode™+K™)—J˜KšΟk œ?žœ}žœ\˜¦K˜šΠbxœžœž˜!Kšžœ4žœ_žœL˜λKšžœ5˜<šž ™ ™/K™)K™—K™)K™5K™OK™/K™GK™K™#K™/——K˜Kšžœžœ˜ K˜Kšœžœžœ˜-šœžœžœ"˜YK˜K˜K˜Kšœ Οc{œž˜‰Kšœ˜—K˜Kšœžœ)˜DK˜KšœžœG˜^K˜Kšœžœ˜ K˜K˜2K˜Kšœ žœ<˜NK˜š Οnœžœžœžœžœ˜0Kšžœžœžœ˜K˜Kšžœ˜—K˜š‘ œžœžœžœ žœžœžœžœ œ˜yKšž˜š‘œžœžœ˜Kšžœžœžœ ,˜GKšœNžœžœ˜YKšœžœžœ˜Kš œžœžœžœžœ˜)K˜Kšœ žœ˜Kšžœžœžœžœžœ:žœžœ˜˜K˜ šžœž˜!Kšœžœ˜ š‘œžœžœžœ˜$K˜)Kšžœžœ*žœ#˜g—š ‘œžœžœžœžœ˜,Kš œžœžœžœžœ!˜6—Kšœ'žœ˜.Kšžœ žœžœ˜Kšœ+˜+Kšžœžœ#žœ˜CKšžœžœžœ˜2Kšžœžœžœžœ˜AKšžœžœžœžœ˜CKšžœžœžœžœ˜CKšžœžœžœžœ˜Cšžœžœ ž˜K˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜šžœ˜ Kšœ.žœ˜@Kšžœ˜——Kšžœ˜—K˜%Kš žœžœžœžœžœ˜fKšžœžœžœ™DKšžœžœh˜‹šžœ?žœžœ˜KKšœ˜Kšœžœ>žœ˜_Kšžœ˜K˜—Kšžœžœ8žœ˜VKšžœžœ9˜RKšžœžœn˜Kšžœžœo˜Žšžœžœžœ(˜8šžœ˜KšœC˜C——Kšžœžœ!˜/Kšœv˜vKšžœ˜—K˜Kšžœ žœžœ<žœ˜VKšžœ˜—K˜š ‘ œžœžœžœž˜4Kšžœžœžœžœžœžœžœžœžœžœ ˜,K˜ Kšžœ ˜—K˜š‘ œžœ œ˜@Kšž˜K˜Kšžœžœžœ#žœ˜=KšžœžœHžœ˜fKšœ+˜+Kšžœ˜—K˜š‘ œžœžœ˜&Kšžœžœžœ ,˜GKšžœ žœžœ˜šžœ(žœžœž˜Kšžœ˜—šžœ:žœžœž˜NKšœ žœ0˜>Kšžœ˜—Kšžœ˜—K˜Kš ‘ œžœžœžœž œ˜NK˜š ‘ œžœžœžœžœ˜;Kšœ˜Kš œžœžœžœžœ˜K—K˜š‘œžœžœžœžœ"žœžœ˜uKšž˜Kšžœr˜xKšžœ˜—K˜š‘ œžœžœžœ˜2Kšž˜K˜ Kšžœ žœžœžœ˜Kšœ˜š žœžœžœžœ#ž˜9Kšœ9žœ˜?—Kšžœ˜—K˜š‘ œžœžœžœ˜AKšž˜Kšœžœ˜Kšœžœ˜%K˜ Kšœžœ˜K˜ Kšœžœ˜!K˜š‘ œžœ˜Kšžœžœžœžœ˜Kšžœ=žœžœžœ˜OKšžœJžœžœžœ˜\KšœXžœžœžœ˜‚Kš žœžœžœžœžœ˜&KšžœKžœžœžœ˜}Kšžœžœžœžœ˜Kšžœ žœžœžœ˜(Kšœžœ"žœ˜