1
//! Authors: Maurice Laveaux and Sjef van Loo
2
use std::fmt;
3

            
4
use itertools::Itertools;
5

            
6
use merc_collections::Graph;
7
use merc_utilities::TagIndex;
8

            
9
use crate::Player;
10

            
11
/// A strong type for the vertices.
12
pub struct VertexTag;
13

            
14
/// A strong type for the priorities.
15
pub struct PriorityTag;
16

            
17
/// The index for a vertex.
18
pub type VertexIndex = TagIndex<usize, VertexTag>;
19

            
20
/// The strong type for a priority.
21
pub type Priority = TagIndex<usize, PriorityTag>;
22

            
23
/// Represents an explicit max-priority parity game. This
24
/// means that higher priority values are more significant.
25
pub struct ParityGame {
26
    /// Stores the owner of every vertex.
27
    owner: Vec<Player>,
28

            
29
    /// Stores the priority of every vertex.
30
    priority: Vec<Priority>,
31

            
32
    /// Offsets into the transition array for every vertex.
33
    vertices: Vec<usize>,
34
    edges_to: Vec<VertexIndex>,
35

            
36
    initial_vertex: VertexIndex,
37
}
38

            
39
impl ParityGame {
40
    /// Construct a new parity game from an iterator over transitions.
41
1307
    pub fn new(
42
1307
        initial_vertex: VertexIndex,
43
1307
        owner: Vec<Player>,
44
1307
        priority: Vec<Priority>,
45
1307
        vertices: Vec<usize>,
46
1307
        edges_to: Vec<VertexIndex>,
47
1307
    ) -> Self {
48
        // Check that the sizes are consistent
49
1307
        debug_assert_eq!(
50
1307
            owner.len(),
51
1307
            priority.len(),
52
            "There should an owner and priority for every vertex"
53
        );
54
1307
        debug_assert_eq!(
55
1307
            vertices.len(),
56
1307
            owner.len() + 1,
57
            "There should be an offset for every vertex, and the sentinel state"
58
        );
59
1307
        debug_assert_eq!(initial_vertex, 0, "The initial vertex should be vertex 0");
60

            
61
1307
        Self {
62
1307
            owner,
63
1307
            priority,
64
1307
            vertices,
65
1307
            edges_to,
66
1307
            initial_vertex,
67
1307
        }
68
1307
    }
69

            
70
    /// Constructs a new parity game from an iterator over edges.
71
    ///
72
    /// The vertices are given by their owner and priority. The `edges` iterator
73
    /// should yield tuples of the form (from, to). If `make_total` is true,
74
    /// self-loops are added on-the-fly to vertices with no outgoing edges.
75
1602
    pub fn from_edges<F, I>(
76
1602
        initial_vertex: VertexIndex,
77
1602
        owner: Vec<Player>,
78
1602
        mut priority: Vec<Priority>,
79
1602
        make_total: bool,
80
1602
        mut edges: F,
81
1602
    ) -> Self
82
1602
    where
83
1602
        F: FnMut() -> I,
84
1602
        I: Iterator<Item = (VertexIndex, VertexIndex)>,
85
    {
86
1602
        let num_of_vertices = owner.len();
87
1602
        debug_assert_eq!(
88
1602
            priority.len(),
89
            num_of_vertices,
90
            "Owner and priority vectors should have the same length"
91
        );
92

            
93
1602
        let mut vertices = Vec::new();
94
1602
        vertices.resize_with(num_of_vertices, Default::default);
95
1602
        debug_assert!(
96
1602
            initial_vertex.value() < num_of_vertices,
97
            "Initial vertex index {} out of bounds {num_of_vertices}",
98
            initial_vertex.value()
99
        );
100

            
101
        // Count the number of transitions for every state
102
1602
        let mut num_of_edges = 0;
103
38583
        for (from, to) in edges() {
104
            // Ensure that the states vector is large enough.
105
38583
            if vertices.len() <= *from.max(to) {
106
                vertices.resize_with(*from.max(to) + 1, || 0);
107
38583
            }
108

            
109
38583
            vertices[*from] += 1;
110
38583
            num_of_edges += 1;
111

            
112
38583
            debug_assert!(
113
38583
                *from < num_of_vertices && *to < num_of_vertices,
114
                "Vertex index out of bounds: from {:?}, to {:?}, num_of_vertices {}",
115
                from,
116
                to,
117
                num_of_vertices
118
            );
119
        }
120

            
121
1602
        if initial_vertex.value() >= vertices.len() {
122
            // Ensure that the initial state is a valid state (and all states before it exist).
123
            vertices.resize_with(initial_vertex.value() + 1, Default::default);
124
1602
        }
125

            
126
        // If make_total is true, reserve space for self-loops on vertices with no outgoing edges
127
1602
        if make_total {
128
26001
            for count in vertices.iter_mut() {
129
26001
                if *count == 0 {
130
2568
                    *count = 1;
131
2568
                    num_of_edges += 1;
132
23433
                }
133
            }
134
200
        }
135

            
136
        // Sets the offset for every state into the edge arrays.
137
28001
        vertices.iter_mut().fold(0, |count, start| {
138
28001
            let result = count + *start;
139
28001
            *start = count;
140
28001
            result
141
28001
        });
142

            
143
        // Place the transitions, and increment the end for every state.
144
1602
        let mut edges_to = vec![VertexIndex::new(0); num_of_edges];
145
38583
        for (from, to) in edges() {
146
38583
            let start = &mut vertices[*from];
147
38583
            edges_to[*start] = to;
148
38583
            *start += 1;
149
38583
        }
150

            
151
        // If make_total is true, add self-loops for vertices that had no outgoing edges
152
1602
        if make_total {
153
26001
            for vertex_idx in 0..num_of_vertices {
154
26001
                let start = vertices[vertex_idx];
155
26001
                let previous = if vertex_idx > 0 { vertices[vertex_idx - 1] } else { 0 };
156
26001
                if start == previous {
157
2568
                    // No outgoing edges, add self-loop
158
2568
                    edges_to[start] = VertexIndex::new(vertex_idx);
159
2568
                    vertices[vertex_idx] += 1; // Increment end offset
160
2568

            
161
2568
                    // Change the priority of the vertex such that the self-loop is winning for the opponent.
162
2568
                    priority[vertex_idx] = Priority::new(owner[vertex_idx].opponent().to_index());
163
23433
                }
164
            }
165
200
        }
166

            
167
        // Reset the offset to the start.
168
28001
        vertices.iter_mut().fold(0, |previous, start| {
169
28001
            let result = *start;
170
28001
            *start = previous;
171
28001
            result
172
28001
        });
173

            
174
1602
        vertices.push(num_of_edges); // Sentinel vertex
175

            
176
1602
        Self {
177
1602
            initial_vertex,
178
1602
            owner,
179
1602
            priority,
180
1602
            vertices,
181
1602
            edges_to,
182
1602
        }
183
1602
    }
184

            
185
    /// Returns the vertices array.
186
113840
    pub(crate) fn vertices(&self) -> &Vec<usize> {
187
113840
        &self.vertices
188
113840
    }
189

            
190
    /// Returns the edges_to array.
191
56920
    pub(crate) fn edges_to(&self) -> &Vec<VertexIndex> {
192
56920
        &self.edges_to
193
56920
    }
194

            
195
    /// Returns the owners array.
196
946
    pub(crate) fn owners(&self) -> &Vec<Player> {
197
946
        &self.owner
198
946
    }
199

            
200
    /// Returns the priorities array.
201
946
    pub(crate) fn priorities(&self) -> &Vec<Priority> {
202
946
        &self.priority
203
946
    }
204
}
205

            
206
impl PG for ParityGame {
207
2049
    fn initial_vertex(&self) -> VertexIndex {
208
2049
        self.initial_vertex
209
2049
    }
210

            
211
18476
    fn num_of_vertices(&self) -> usize {
212
18476
        self.owner.len()
213
18476
    }
214

            
215
2604
    fn num_of_edges(&self) -> usize {
216
2604
        self.edges_to.len()
217
2604
    }
218

            
219
8441
    fn iter_vertices(&self) -> impl Iterator<Item = VertexIndex> + '_ {
220
8441
        (0..self.num_of_vertices()).map(VertexIndex::new)
221
8441
    }
222

            
223
29691
    fn outgoing_edges(&self, state_index: VertexIndex) -> impl Iterator<Item = VertexIndex> + '_ {
224
29691
        let start = self.vertices[*state_index];
225
29691
        let end = self.vertices[*state_index + 1];
226

            
227
34702
        (start..end).map(move |i| self.edges_to[i])
228
29691
    }
229

            
230
37274
    fn owner(&self, vertex: VertexIndex) -> Player {
231
37274
        self.owner[*vertex]
232
37274
    }
233

            
234
42384
    fn priority(&self, vertex: VertexIndex) -> Priority {
235
42384
        self.priority[*vertex]
236
42384
    }
237

            
238
    fn highest_priority(&self) -> Priority {
239
        self.priority.iter().fold(Priority::new(0), |max, p| max.max(*p))
240
    }
241

            
242
1201
    fn is_total(&self) -> bool {
243
        // The parity game is total if every vertex has at least one outgoing edge.
244
8379
        self.iter_vertices().all(|v| self.outgoing_edges(v).next().is_some())
245
1201
    }
246
}
247

            
248
impl fmt::Debug for ParityGame {
249
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250
        writeln!(f, "ParityGame {{")?;
251
        writeln!(f, "  num_vertices: {},", self.num_of_vertices())?;
252
        writeln!(f, "  num_edges: {},", self.num_of_edges())?;
253
        writeln!(f, "  initial_vertex: v{},", *self.initial_vertex)?;
254
        writeln!(f, "  vertices: [")?;
255
        for v in self.iter_vertices() {
256
            let owner = self.owner(v);
257
            let prio = self.priority(v);
258
            write!(
259
                f,
260
                "    {}: ({:?}, priority: {}, outgoing: [",
261
                *v,
262
                owner.to_index(),
263
                *prio
264
            )?;
265

            
266
            write!(f, "{}", self.outgoing_edges(v).format(", "))?;
267
            writeln!(f, "]),")?;
268
        }
269
        writeln!(f, "  ]")?;
270
        writeln!(f, "}}")
271
    }
272
}
273

            
274
/// A trait for parity games.
275
pub trait PG {
276
    /// Returns the initial vertex of the parity game.
277
    fn initial_vertex(&self) -> VertexIndex;
278

            
279
    /// Returns the number of vertices in the parity game.
280
    fn num_of_vertices(&self) -> usize;
281

            
282
    /// Returns the number of edges in the parity game.
283
    fn num_of_edges(&self) -> usize;
284

            
285
    /// Returns the highest priority in the parity game.
286
    fn highest_priority(&self) -> Priority;
287

            
288
    /// Returns an iterator over all vertices in the parity game.
289
    fn iter_vertices(&self) -> impl Iterator<Item = VertexIndex> + '_;
290

            
291
    /// Returns an iterator over the outgoing edges for the given vertex.
292
    fn outgoing_edges(&self, state_index: VertexIndex) -> impl Iterator<Item = VertexIndex> + '_;
293

            
294
    /// Returns the owner of the given vertex.
295
    fn owner(&self, vertex: VertexIndex) -> Player;
296

            
297
    /// Returns the priority of the given vertex.
298
    fn priority(&self, vertex: VertexIndex) -> Priority;
299

            
300
    /// Returns true iff the parity game is total, checks all vertices have at least one outgoing edge.
301
    fn is_total(&self) -> bool;
302
}
303

            
304
/// A wrapper to view a parity game as a graph.
305
pub struct AsGraph<'a, G: PG>(pub &'a G);
306

            
307
/// Every parity game is also a graph.
308
impl<G: PG> Graph for AsGraph<'_, G> {
309
    type VertexIndex = VertexIndex;
310
    type LabelIndex = ();
311

            
312
    fn num_of_vertices(&self) -> usize {
313
        self.0.num_of_vertices()
314
    }
315

            
316
    fn iter_vertices(&self) -> impl Iterator<Item = Self::VertexIndex> {
317
        self.0.iter_vertices()
318
    }
319

            
320
    fn outgoing_edges(
321
        &self,
322
        vertex: <Self as Graph>::VertexIndex,
323
    ) -> impl Iterator<Item = (Self::LabelIndex, Self::VertexIndex)> {
324
        self.0.outgoing_edges(vertex).map(|to| ((), to))
325
    }
326
}
327

            
328
#[cfg(test)]
329
mod tests {
330
    use merc_utilities::random_test;
331

            
332
    use crate::PG;
333
    use crate::random_parity_game;
334

            
335
    #[test]
336
1
    fn test_random_parity_game_make_total() {
337
100
        random_test(100, |rng| {
338
100
            let game = random_parity_game(rng, true, 50, 10, 5);
339
100
            assert!(game.is_total());
340
100
        });
341
1
    }
342
}