;redcode-94 ;name Psst ;author Robert Macrae ;strategy Adaptive play based on frequencies of opponent choices ;strategy conditioned on previous round. Use random numbers only as ;strategy last resort. Handshake for 1000/0/0 against self. ;strategy (I'd suggest NO self fights this round...) ;strategy Hope other players can't write random number generators :> ; I did not have time to integrate imp spiral, but smell that garlic :) ; Hope that _first_ score counts in self-fights! ;Warrior that plays the game of Paper-Scissors-Stone in NSFCWT round 5 ;Needs to be paired with one opponent exactly half the coresize apart ;assert WARRIORS==1 || (WARRIORS == 2 && MINDISTANCE == CORESIZE/2) ;assert VERSION >= 80 ;my/your_hand are used to communicate between players your_hand equ (my_hand+CORESIZE/2) PAPER equ #1 ;some useful constants SCISSORS equ #2 STONE equ #3 org start my_hand dat 0,0 ; Variable part starts here. This is were I decide what hand to ; use next. I can use the value of the RESULTS cell (0 if lost, ; 1 if won, 2 if tied last round) and any other values I chose ; to store in pspace. The hand I decide to show is stored in the ; B-field of instruction 'my_hand'. ;---BEGIN-VARIABLE-PART----------------------------------------------------- ; P-Space Variables. Note I also use an array [1..3,1..3,1..3] which maps ; onto 21 to 63. Spurious accesses will occur in the first two rounds, so ; I keep other variables up above this space. _Result EQU #0 _Rounds EQU #71 _Me1 EQU #72 _Me2 EQU #73 _Him2 EQU #74 _STRAT EQU #75 _SlowP EQU #76 _IsSlow EQU #77 _Friend EQU #78 _Rand1 EQU #79 _Rand2 EQU #80 _Score EQU #81 ; -------------------------------------------------------------------------- start Top1 JMP UpdateFreq ; Collects result and updates frequencies Top11 SNE Result, #-1 ; On first move JMP HandShake ; check for self-fights Top2 JMP HandleSlow ; Checks for slow cycle and if found, moves Top3 SEQ IsSlow, #1 ; Unless I believe slow cycle model JMP CollectCounts ; make best move based on past frequencies Top4 SNE Rounds, #1 ; If second round JMP MoveTwo ; apply special rule Top41 JMP Random ; Make random move if I'm losing badly Top5 JMP DoMove ; Check handshake, and, not play and exit. MyPlay DAT #0, #0 ; (Global variable?!) ; -------------------------------------------------------------------------- UpdateFreq Result LDP _Result, #0 Rounds LDP _Rounds, #0 Me1 LDP _Me1, #0 ; My last play Me2 LDP _Me2, #0 ; and preceding. Him2 LDP _Him2, #0 MOV.b Me1, Him1 ADD.b Result, Him1 Him1 MOD.ab #3, #0 ADD.ab #1, Him1 ; Deduce his last from Result and my last SNE.ab #-1, Result MOV.ab #0, Him1 ; Correct for first round Ptr MUL.b Him2, #16 MUL.b Me2, #4 ADD.b -1, Ptr ADD.b Him1, Ptr ; Use past moves to index into an array MOV.ba Ptr, GetCnt MOV.b Ptr, StorCnt GetCnt LDP #0, #0 ; and get appropriate frequency ADD.ba GetCnt, StorCnt StorCnt STP #1, #0 ; increment and store. SNE.ab #-1, Result STP #1000, _Score ; Initialise _Score to 1000 GetScor LDP _Score, #0 ; Get cumulative score (so I can tell if MOV.ba GetScor, StoScor ; I should play randomly) SNE.ab #1, Result ADD.a #1, StoScor SNE.ab #0, Result ADD.a #-1, StoScor StoScor STP #0, _Score ; update and store. JMP Top11 ; 'Return' ; -------------------------------------------------------------------------- HandShake MOV Hand, start+1158 ; Store a flag SEQ start+1158, start+1158+4000 ; If opponent didn't, exit JMP Top2 STP #1, _Friend ; Flag fact we're friends SEQ @start+1158+4000, >start+1158 ; If I move second, then JMP Top2 ; return and play normally. MOV.ab #1, MyPlay ; Otherwise play paper JMP Last ; (Structure be damned). ; This method is messy. I hit some problem with both versions sharing ; P-Space, so I couldn't simply store an instruction on what to play. ; Having "fixed" the first round as a win, I then fix all subsequent ; rounds so that the previous winner wins again. Hand DAT >2667, #4000 ; .B points to to other flag. ; -------------------------------------------------------------------------- HandleSlow ; Runs a copy of the slow-rotation code and bets with it if it has worked ; 5 times or more _and_ has never failed. IsSlow LDP _IsSlow, #0 SNE.ab #2, IsSlow JMP Top3 ; Slow prediction has failed in the past. SNE.ab #5, Rounds ; After 5 hits, move from observing MOV #1, IsSlow ; (IsSlow=0) to acting (IsSlow=1) SlowP LDP _SlowP, #0 ; Get last prediction SEQ.b SlowP, Him1 ; Did slow prediction succeed MOV #2, IsSlow ; Store the fact that slow prediction failed. strgy ldp _STRAT, #strgy add #1, strgy ; next strategy no matter what stp strgy, _STRAT ; but slowly div #5, strgy mod #3, strgy ; 1111222223333311111.. store add.ab #1, strgy mov.ba strgy, StrSlP StrSlP STP #0, _SlowP ; Store prediction for next time MOD.ab #3, strgy ADD.ab #1, strgy ; convert prediction to winning play SNE IsSlow, #1 MOV strgy, MyPlay ; Make play if slow prediction is working. MOV.ba IsSlow, StorSlo StorSlo STP #0, _IsSlow ; Store IsSlow status JMP Top3 ; 'Return' ; -------------------------------------------------------------------------- CollectCounts Ptr2 MUL.b Him1, #16 ; Find appropriate row of array MUL.b Me1, #4 ADD.b -1, Ptr2 ADD.ba Ptr2, PapC ; and get all 3 alternatives ADD.ba Ptr2, SciC ADD.ba Ptr2, StoC PapC LDP #1, #0 ; This gets frequencies conditioned on SciC LDP #2, #0 ; Me1 and Him1 ie on the last round. StoC LDP #3, #0 PapSc MUL.b StoC, #3 ; Anticipated score for playing paper ADD.b PapC, PapSc SciSc MUL.b PapC, #3 ; ... scissors ADD.b SciC, SciSc StoSc MUL.b SciC, #3 ; ... and stone ADD.b StoC, StoSc BestSc MOV PapSc, 0 ; Find the best score SLT.b SciSc, BestSc MOV SciSc, BestSc SLT.b StoSc, BestSc MOV StoSc, BestSc SNE.b PapSc, BestSc ; Store the best move (if tie, later) MOV #1, MyPlay ; in MyPlay SNE.b SciSc, BestSc MOV #2, MyPlay SNE.b StoSc, BestSc MOV #3, MyPlay OddRnd MOV.b Rounds, 0 MOD.ab #2, OddRnd JMZ Top4, OddRnd ; On even rounds, Return SNE.b SciSc, BestSc ; On odd rounds, use earlier of ties MOV #2, MyPlay ; (to avoid bias towards stone.) SNE.b PapSc, BestSc MOV #1, MyPlay JMP Top4 ; 'Return' ; -------------------------------------------------------------------------- MoveTwo MOV.b Him1, MyPlay ; Get his first move MOD.ab #3, MyPlay ; and play whatever beats it. ADD.ab #1, MyPlay ; Speeds lock on constant strategies. JMP Top41 ; 'Return' ; -------------------------------------------------------------------------- Random SLT.ab #100, Rounds ; If less than 100 rounds JMP Top5 SLT.ab StoScor, #990 ; or I am less than 10 behind JMP Top5 ; then keep trying to predict. ; otherwise, generate a random play. Rand1 LDP _Rand1, #0 ; Linear congruential generators, so Rand2 LDP _Rand2, #0 ; don't need initialising (I hope) MOV.b Rand1, Count Count MOD #5, #0 ; Number of times to churn generator Reroll MUL #39, Rand1 ; Roll those dice (#39 is a not as ADD #13, Rand1 ; prime as I thought.) MOD #73, Rand1 MUL #31, Rand2 ; Two different, weak generators will ADD #23, Rand2 ; be stronger when combined... MOD #71, Rand2 SUB.ab #1, Count SLT.ab #6, Count JMP Reroll, Count ; Churn 1-5 times. MOV.ba Rand1, StRand1 MOV.ba Rand2, StRand2 StRand1 STP #0, _Rand1 StRand2 STP #0, _Rand2 ; Update state Out ADD.b Rand1, #71 SUB.b Rand2, Out MOD.ab #72, Out ; Combine (should be 71 or 73?) DIV.ab #8, Out ; Drop tail of low digit MOD.ab #3, Out ; Drop head of high digit ADD.ab #1, Out ; Convert to a play MOV.b Out, MyPlay ; and store JMP Top5 ; -------------------------------------------------------------------------- DoMove MOV.ba MyPlay, StMe1 ; Update record of plays MOV.ba Me1, StMe2 ; for last two games MOV.ba Him1, StHim2 ADD.ba Rounds, IncRnd ; Update round counter IncRnd STP #1, _Rounds StMe1 STP #0, _Me1 ; Store state StMe2 STP #0, _Me2 StHim2 STP #0, _Him2 Friend LDP _Friend, #0 JMZ Last, Friend ; If I am playing myself, SNE.b Result, #1 MOV.ab #2, MyPlay ; Winner plays scissors SNE.b Result, #0 MOV.ab #1, MyPlay ; Loser plays paper Last MOV.b MyPlay, my_hand ; Make the play! ;---END-VARIABLE PART------------------------------------------------------- ; End of variable part. I wait for the other player to show his hand. ; I then analyze the outcome of the game. ; wait jmz.b wait,your_hand work mov.b your_hand,#work sub.b my_hand,work ;your_hand minus my_hand, loss=1,-2 add #2,work ;loss=3,0 mod #3,work ;loss=0 live jmn live,work ;die if lost, live if won or tied end I hope I have defeated (defeatured? debugged?) the word-wrap this time... Regards, Robert Macrae