-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbattleship.frg
154 lines (134 loc) · 3.92 KB
/
battleship.frg
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
#lang forge/bsl
one sig Game {
initialState: one State,
next: pfunc State -> State
}
one sig Yes {}
abstract sig Ship {
length: one Int,
isOccupying: pfunc Int -> Int -> Yes
}
sig Destroyer extends Ship {}
sig Battleship extends Ship {}
sig Submarine extends Ship {}
abstract sig Fleet {
destroyer: one Destroyer,
battleship: one Battleship,
submarine: one Submarine
}
--A occupies top half of board, B bottom half
one sig A, B extends Fleet {}
--board is 20x10 map of attacks
sig State {
board: pfunc Int -> Int -> Fleet
}
pred wellformed {
--ships are within the bounds of the respective board
all row, col: Int | {
(some A.destroyer.isOccupying[row][col] or
some A.battleship.isOccupying[row][col] or
some A.submarine.isOccupying[row][col]) => {
row >= 0 and row < 5
col >= 0 and col < 5
}
(some B.destroyer.isOccupying[row][col] or
some B.battleship.isOccupying[row][col] or
some B.submarine.isOccupying[row][col]) => {
row >= 0 and row < 5
col >= 5 and col < 10
}
}
--ships are of proper length and composed of adjacent coords
all s: Ship | {
#{row, col: Int | some s.isOccupying[row][col]} = s.length
all r1, r2, c1, c2: Int | {
(some s.isOccupying[r1][c1] and some s.isOccupying[r2][c2]) => {
(abs[r1 - r2] < s.length and c1 = c2) or
(abs[c1 - c2] < s.length and r1 = r2)
}
}
}
--ships are not overlapping
all disj s1, s2: Ship | {
all row, col: Int | {
some s1.isOccupying[row][col] =>
no s2.isOccupying[row][col]
}
}
}
pred lengths {
all f: Fleet | {
f.destroyer.length = 2
f.submarine.length = 3
f.battleship.length = 4
}
}
pred init[s: State] {
-- all board outputs are none
all row, col: Int | {
no s.board[row][col]
}
}
pred ATurn[s: State] {
#{row, col: Int | s.board[row][col] = A} =
#{row, col: Int | s.board[row][col] = B}
}
pred BTurn[s: State] {
#{row, col: Int | s.board[row][col] = A} =
add[#{row, col: Int | s.board[row][col] = B}, 1]
}
pred validTransition[pre: State, post: State] {
--all attacks from pre state are present in post
all row, col: Int | {
some pre.board[row][col] => some post.board[row][col]
}
#{row, col: Int | some post.board[row][col]} = add[1, #{row, col: Int | some pre.board[row][col]}]
--there's one new attack and it's valid
one row, col: Int | {
some post.board[row][col]
no pre.board[row][col]
post.board[row][col] = A => {
ATurn[pre]
row >= 0 and row < 5
col >= 5 and col < 10
}
else {
BTurn[pre]
row >= 0 and row < 5
col >= 0 and col < 5
}
}
}
pred gameOver[s : State] {
some disj w, l: Fleet | {
all row, col: Int | {
(some l.destroyer.isOccupying[row][col] or
some l.battleship.isOccupying[row][col] or
some l.submarine.isOccupying[row][col]) =>
s.board[row][col] = w
}
}
}
pred doNothing[pre: State, post: State] {
gameOver[pre] -- guard of the transition
pre.board = post.board -- effect of the transition
}
pred traces {
-- The trace starts with an initial state
init[Game.initialState]
no sprev: State | Game.next[sprev] = Game.initialState
-- Every transition is a valid move
all s: State | some Game.next[s] implies {
validTransition[s, Game.next[s]] or
doNothing[s, Game.next[s]]
}
}
// example validStart is init for {
// }
-- traces of State
run {
wellformed
traces
lengths
} for exactly 2 Destroyer, exactly 2 Battleship, exactly 2 Submarine,
5 Int, 3 State for {next is linear}