-
Notifications
You must be signed in to change notification settings - Fork 9
/
Rudimentary.hs
154 lines (132 loc) · 5.49 KB
/
Rudimentary.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
module Rudimentary where
import Botworld
import Botworld.Display
import Control.Monad.Reader (runReader)
import Data.Map (Map, fromList)
-- Simple programs
-- Below is a function that allows us to create robots that execute a hardcoded
-- list of actions using seven registers. (These robots must be run on
-- a processor that has speed 3 or greater.)
-- Our simple programs use seven registers. These include:
rPRG = 0 -- Program register (builtin)
rINP = 1 -- Input register (builtin)
rOUT = 2 -- Output register (builtin)
rNIL = 3 -- Nil register (will always be nil)
rSPN = 4 -- Spin register (holds a program that spins until output is read)
rCTR = 5 -- Control register (holds the main program while spinning)
rQUE = 6 -- Queue register (holds the upcoming commands)
-- This helper function that creates a register of exactly the right size for
-- its contents.
reg :: Encodable i => i -> Register
reg i = let t = encode i in R (size t) t
-- This function builds a 7-register machine that hardcodes its commands.
-- Basically, it holds a list of commands it wants to execute in the QUE
-- register. The main program deconstructs the QUE register putting the head in
-- OUT and the rest back in QUE, then loads the SPN program. The SPN program
-- spins until OUT becomes Nil, then loads the main program back up. Rinse,
-- repeat.
hardcode :: [Command] -> Memory
hardcode [] = []
hardcode commands = registers where
registers = [program, input, output, nil, spinner, queuer, queue]
psize = max (size $ contents queuer) (size $ contents spinner)
program = R psize (contents spinner)
spinner = reg [CopyIfNil rOUT rCTR rPRG, CopyIfNil rNIL rSPN rPRG]
input = R 0 Nil
outputs = map encode commands
output = R (maximum $ map size outputs) (head outputs)
nil = R 0 Nil
queuer = reg [Deconstruct rQUE rOUT rQUE, CopyIfNil rNIL rSPN rPRG]
queue = reg $ tail outputs
-- This function is useful when you're debugging a program and want to simulate
-- that OUT has been read. (Remember that OUT is zeroed after it is read.)
zeroOUT :: Memory -> Memory
zeroOUT = alter 2 (forceR Nil)
-- This function runs a memory and assumes that the run succeeds.
-- (If it fails, the function crashes.)
unsafeRun :: Int -> Memory -> Memory
unsafeRun n m = let Right m' = runFor n m in m'
-- This function dumps the contents of a memory IF the memory was created with
-- the hardcode function above. Otherwise, it crashes.
unsafeContents :: Memory -> String
unsafeContents (p:i:o:n:s:c:q:[]) = result where
result = unlines
[ "PRG: " ++ show prg
, "INP: " ++ show inp
, "OUT: " ++ out
, "NIL: " ++ show nil
, "SPN: " ++ show spn
, "CTR: " ++ show ctr
, "QUE: " ++ show que
]
Just prg = decode (contents p) :: Maybe [Instruction]
inp = contents i
out = maybe (show $ contents o) show (decode (contents o) :: Maybe Command)
nil = contents n
Just spn = decode (contents s) :: Maybe [Instruction]
Just ctr = decode (contents c) :: Maybe [Instruction]
Just que = decode (contents q) :: Maybe [Command]
-- This function is like the above function, but it prints the contents of the
-- memory to IO (instead of just turning it into a string).
unsafeCheck :: Memory -> IO ()
unsafeCheck = putStr . unsafeContents
-- World building
-- Adds a robot to a square.
addRobot :: Robot -> Maybe Square -> Maybe Square
addRobot r = fmap (\(Square rs is) -> Square (r:rs) is)
-- Example game:
-- Only cargo is valued.
simpleValuer :: Item -> Int
simpleValuer (Cargo v _) = v
simpleValuer _ = 0
-- There is only one player, Player 1. Their home square is top-left.
players :: Map String Player
players = fromList [("Player 1", Player standardValuer (0, 0))]
-- These are the cargos in the initial world.
sampleCargoes :: [[Item]]
sampleCargoes =
[ [Cargo 2 3, Cargo 1 2, Cargo 1 2, Cargo 9 1, Cargo 1 1]
, [Cargo 3 3, Cargo 3 3, Cargo 9 3, Cargo 6 1, Cargo 5 2]
, [Cargo 2 2, Cargo 10 1, Cargo 3 3, Cargo 9 3, Cargo 4 2]
, [Cargo 1 2, Cargo 6 3, Cargo 2 3, Cargo 1 2, Cargo 3 2]
, [Cargo 5 2, Cargo 6 1, Cargo 6 1, Cargo 8 2, Cargo 1 3]
]
uninhabitedWorld :: Botworld
uninhabitedWorld = generate (5, 5) gen where
gen (x, y) = Just $ Square [] [sampleCargoes !! y !! x]
-- The lifter robot tries to lift a few boxes and then return to the home
-- square.
lifter :: Robot
lifter = Robot (F Red 10) [] (P 10) $ hardcode
[Lift 0, Move S, Lift 0, Move E, Move S, Lift 0 , Move N, Move NW, Pass]
-- The aggressor tries to destroy the lifter.
aggressor :: Robot
aggressor = Robot (F Green 200) [] (P 10) $ hardcode
[Pass, Pass, Destroy 1, Build [0..12] $ hardcode [Move W]]
-- The overwriter tries to rebuild the aggressor into a nicer robot (that walks
-- away).
overwriter :: Robot
overwriter = Robot (F Blue 0) [] (P 10) $ hardcode
[Move N, Destroy 0, Build [1..9] $ hardcode [Move S, Move S]]
-- Here is a world with all three robots.
populatedWorld :: Botworld
populatedWorld =
change (addRobot lifter) (0, 0) $
change (addRobot aggressor) (0, 1) $
change (addRobot overwriter) (0, 2)
uninhabitedWorld
-- This infinite list contains all updates of the initial world. (We'll look at
-- the first ten or so.)
evolution :: [Botworld]
evolution = iterate update populatedWorld
-- When run, this file prints out the initial state, the final state, and
-- a scoreboard.
main :: IO ()
main = do
let initialWorld = head evolution
displayBotworld players initialWorld
mapM_ (displayEventGrid players . runEnvironment) (take 9 evolution)
let finalWorld = evolution !! 10
displayBotworld players finalWorld
putStrLn ""
displayScoreboard players finalWorld