quantrs2_core/
quantum_game_theory.rs

1//! Quantum Game Theory
2//!
3//! This module implements quantum game theory algorithms and protocols,
4//! extending classical game theory to the quantum realm. It includes
5//! quantum Nash equilibria, quantum strategies, and quantum mechanisms
6//! for multi-player games.
7use crate::error::{QuantRS2Error, QuantRS2Result};
8use scirs2_core::ndarray::{Array1, Array2};
9use scirs2_core::Complex64;
10use std::collections::HashMap;
11/// Types of quantum games
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum GameType {
14    /// Prisoner's Dilemma with quantum strategies
15    QuantumPrisonersDilemma,
16    /// Battle of the Sexes with quantum entanglement
17    QuantumBattleOfSexes,
18    /// Quantum Auction mechanism
19    QuantumAuction,
20    /// Quantum Coordination Game
21    QuantumCoordination,
22    /// Quantum Minority Game
23    QuantumMinorityGame,
24    /// Custom quantum game
25    Custom,
26}
27/// Player strategies in quantum games
28#[derive(Debug, Clone, PartialEq)]
29pub enum QuantumStrategy {
30    /// Classical deterministic strategy (pure classical)
31    Classical(f64),
32    /// Quantum superposition strategy
33    Superposition { theta: f64, phi: f64 },
34    /// Entangled strategy (requires coordination with other player)
35    Entangled,
36    /// Mixed quantum strategy
37    Mixed(Vec<(f64, Self)>),
38}
39/// Quantum player in a game
40#[derive(Debug, Clone)]
41pub struct QuantumPlayer {
42    /// Player ID
43    pub id: usize,
44    /// Player's quantum strategy
45    pub strategy: QuantumStrategy,
46    /// Player's quantum state (for entangled strategies)
47    pub state: Option<Array1<Complex64>>,
48    /// Player's utility function parameters
49    pub utility_params: HashMap<String, f64>,
50}
51impl QuantumPlayer {
52    /// Create a new quantum player
53    pub fn new(id: usize, strategy: QuantumStrategy) -> Self {
54        Self {
55            id,
56            strategy,
57            state: None,
58            utility_params: HashMap::new(),
59        }
60    }
61    /// Set utility function parameter
62    pub fn set_utility_param(&mut self, param: String, value: f64) {
63        self.utility_params.insert(param, value);
64    }
65    /// Prepare quantum state based on strategy
66    pub fn prepare_quantum_state(&mut self) -> QuantRS2Result<Array1<Complex64>> {
67        match self.strategy {
68            QuantumStrategy::Classical(theta) => {
69                let state = Array1::from_vec(vec![
70                    Complex64::new(theta.cos(), 0.0),
71                    Complex64::new(theta.sin(), 0.0),
72                ]);
73                self.state = Some(state.clone());
74                Ok(state)
75            }
76            QuantumStrategy::Superposition { theta, phi } => {
77                let state = Array1::from_vec(vec![
78                    Complex64::new((theta / 2.0).cos(), 0.0),
79                    Complex64::new(
80                        (theta / 2.0).sin() * phi.cos(),
81                        (theta / 2.0).sin() * phi.sin(),
82                    ),
83                ]);
84                self.state = Some(state.clone());
85                Ok(state)
86            }
87            QuantumStrategy::Entangled => {
88                let state = Array1::from_vec(vec![
89                    Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
90                    Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
91                ]);
92                self.state = Some(state.clone());
93                Ok(state)
94            }
95            QuantumStrategy::Mixed(_) => {
96                let state = Array1::from_vec(vec![
97                    Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
98                    Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0),
99                ]);
100                self.state = Some(state.clone());
101                Ok(state)
102            }
103        }
104    }
105    /// Compute expected utility given game outcomes
106    pub fn compute_utility(&self, game_outcome: &GameOutcome) -> f64 {
107        match &game_outcome.payoff_matrix {
108            Some(payoffs) => payoffs.iter().sum::<f64>() / payoffs.len() as f64,
109            None => 0.0,
110        }
111    }
112}
113/// Game outcome after quantum measurement
114#[derive(Debug, Clone)]
115pub struct GameOutcome {
116    /// Classical outcome for each player
117    pub classical_outcomes: Vec<usize>,
118    /// Quantum measurement probabilities
119    pub probabilities: Array1<f64>,
120    /// Payoff matrix for the outcomes
121    pub payoff_matrix: Option<Vec<f64>>,
122    /// Nash equilibrium indicator
123    pub is_nash_equilibrium: bool,
124}
125/// Quantum game engine
126#[derive(Debug, Clone)]
127pub struct QuantumGame {
128    /// Type of game
129    pub game_type: GameType,
130    /// Number of players
131    pub num_players: usize,
132    /// Players in the game
133    pub players: Vec<QuantumPlayer>,
134    /// Quantum circuit for the game
135    pub game_circuit: Option<Array2<Complex64>>,
136    /// Payoff matrices (one for each player)
137    pub payoff_matrices: Vec<Array2<f64>>,
138    /// Entanglement operator (if applicable)
139    pub entanglement_operator: Option<Array2<Complex64>>,
140}
141impl QuantumGame {
142    /// Create a new quantum game
143    pub fn new(game_type: GameType, num_players: usize) -> Self {
144        let players = Vec::new();
145        let payoff_matrices = vec![Array2::zeros((2, 2)); num_players];
146        Self {
147            game_type,
148            num_players,
149            players,
150            game_circuit: None,
151            payoff_matrices,
152            entanglement_operator: None,
153        }
154    }
155    /// Add a player to the game
156    pub fn add_player(&mut self, player: QuantumPlayer) -> QuantRS2Result<()> {
157        if self.players.len() >= self.num_players {
158            return Err(QuantRS2Error::InvalidInput(
159                "Maximum number of players reached".to_string(),
160            ));
161        }
162        self.players.push(player);
163        Ok(())
164    }
165    /// Set payoff matrix for a specific player
166    pub fn set_payoff_matrix(
167        &mut self,
168        player_id: usize,
169        payoffs: Array2<f64>,
170    ) -> QuantRS2Result<()> {
171        if player_id >= self.num_players {
172            return Err(QuantRS2Error::InvalidInput(
173                "Player ID out of bounds".to_string(),
174            ));
175        }
176        self.payoff_matrices[player_id] = payoffs;
177        Ok(())
178    }
179    /// Create quantum prisoner's dilemma
180    pub fn quantum_prisoners_dilemma() -> QuantRS2Result<Self> {
181        let mut game = Self::new(GameType::QuantumPrisonersDilemma, 2);
182        let payoff_p1 = Array2::from_shape_vec((2, 2), vec![3.0, 0.0, 5.0, 1.0]).map_err(|e| {
183            QuantRS2Error::InvalidInput(format!("Failed to create payoff matrix: {e}"))
184        })?;
185        let payoff_p2 = Array2::from_shape_vec((2, 2), vec![3.0, 5.0, 0.0, 1.0]).map_err(|e| {
186            QuantRS2Error::InvalidInput(format!("Failed to create payoff matrix: {e}"))
187        })?;
188        game.set_payoff_matrix(0, payoff_p1)?;
189        game.set_payoff_matrix(1, payoff_p2)?;
190        let entanglement = Self::create_entanglement_operator(std::f64::consts::PI / 2.0)?;
191        game.entanglement_operator = Some(entanglement);
192        Ok(game)
193    }
194    /// Create quantum coordination game
195    pub fn quantum_coordination_game() -> QuantRS2Result<Self> {
196        let mut game = Self::new(GameType::QuantumCoordination, 2);
197        let payoff_p1 = Array2::from_shape_vec((2, 2), vec![2.0, 0.0, 0.0, 1.0]).map_err(|e| {
198            QuantRS2Error::InvalidInput(format!("Failed to create payoff matrix: {e}"))
199        })?;
200        let payoff_p2 = payoff_p1.clone();
201        game.set_payoff_matrix(0, payoff_p1)?;
202        game.set_payoff_matrix(1, payoff_p2)?;
203        Ok(game)
204    }
205    /// Create quantum auction mechanism
206    pub fn quantum_auction(num_bidders: usize) -> QuantRS2Result<Self> {
207        let mut game = Self::new(GameType::QuantumAuction, num_bidders);
208        for i in 0..num_bidders {
209            let payoff = Array2::from_shape_vec((2, 2), vec![1.0, 0.5, 2.0, 0.0]).map_err(|e| {
210                QuantRS2Error::InvalidInput(format!("Failed to create payoff matrix: {e}"))
211            })?;
212            game.set_payoff_matrix(i, payoff)?;
213        }
214        Ok(game)
215    }
216    /// Create entanglement operator
217    fn create_entanglement_operator(gamma: f64) -> QuantRS2Result<Array2<Complex64>> {
218        let cos_g = (gamma / 2.0).cos();
219        let sin_g = (gamma / 2.0).sin();
220        Array2::from_shape_vec(
221            (4, 4),
222            vec![
223                Complex64::new(cos_g, 0.0),
224                Complex64::new(0.0, 0.0),
225                Complex64::new(0.0, 0.0),
226                Complex64::new(0.0, sin_g),
227                Complex64::new(0.0, 0.0),
228                Complex64::new(cos_g, 0.0),
229                Complex64::new(0.0, -sin_g),
230                Complex64::new(0.0, 0.0),
231                Complex64::new(0.0, 0.0),
232                Complex64::new(0.0, -sin_g),
233                Complex64::new(cos_g, 0.0),
234                Complex64::new(0.0, 0.0),
235                Complex64::new(0.0, sin_g),
236                Complex64::new(0.0, 0.0),
237                Complex64::new(0.0, 0.0),
238                Complex64::new(cos_g, 0.0),
239            ],
240        )
241        .map_err(|e| {
242            QuantRS2Error::InvalidInput(format!("Failed to create entanglement operator: {e}"))
243        })
244    }
245    /// Play the quantum game and return outcome
246    pub fn play_game(&mut self) -> QuantRS2Result<GameOutcome> {
247        if self.players.len() != self.num_players {
248            return Err(QuantRS2Error::InvalidInput(
249                "Not all players have joined the game".to_string(),
250            ));
251        }
252        let mut joint_state = self.prepare_joint_state()?;
253        if let Some(entanglement_op) = &self.entanglement_operator {
254            joint_state = entanglement_op.dot(&joint_state);
255        }
256        joint_state = self.apply_player_strategies(joint_state)?;
257        self.measure_game_outcome(joint_state)
258    }
259    /// Prepare joint quantum state of all players
260    fn prepare_joint_state(&mut self) -> QuantRS2Result<Array1<Complex64>> {
261        let total_dim = 1 << self.num_players;
262        let mut joint_state = Array1::zeros(total_dim);
263        joint_state[0] = Complex64::new(1.0, 0.0);
264        let mut player_states = Vec::new();
265        for player in &mut self.players {
266            let player_state = player.prepare_quantum_state()?;
267            player_states.push(player_state);
268        }
269        for (i, player_state) in player_states.into_iter().enumerate() {
270            joint_state = Self::tensor_product_player_state(joint_state, player_state, i)?;
271        }
272        Ok(joint_state)
273    }
274    /// Tensor product of joint state with single player state
275    fn tensor_product_player_state(
276        joint_state: Array1<Complex64>,
277        player_state: Array1<Complex64>,
278        player_index: usize,
279    ) -> QuantRS2Result<Array1<Complex64>> {
280        let mut new_state = joint_state;
281        for i in 0..new_state.len() {
282            let bit = (i >> player_index) & 1;
283            if bit == 0 {
284                new_state[i] *= player_state[0];
285            } else {
286                new_state[i] *= player_state[1];
287            }
288        }
289        Ok(new_state)
290    }
291    /// Apply all player strategies to the joint state
292    fn apply_player_strategies(
293        &self,
294        mut joint_state: Array1<Complex64>,
295    ) -> QuantRS2Result<Array1<Complex64>> {
296        for (i, player) in self.players.iter().enumerate() {
297            joint_state = Self::apply_single_player_strategy(joint_state, &player.strategy, i)?;
298        }
299        Ok(joint_state)
300    }
301    /// Apply a single player's strategy to the joint state
302    fn apply_single_player_strategy(
303        mut joint_state: Array1<Complex64>,
304        strategy: &QuantumStrategy,
305        player_index: usize,
306    ) -> QuantRS2Result<Array1<Complex64>> {
307        match strategy {
308            QuantumStrategy::Classical(theta) => {
309                let cos_theta = theta.cos();
310                let sin_theta = theta.sin();
311                for i in 0..joint_state.len() {
312                    let bit = (i >> player_index) & 1;
313                    if bit == 1 {
314                        joint_state[i] *= Complex64::new(cos_theta, sin_theta);
315                    }
316                }
317            }
318            QuantumStrategy::Superposition { theta, phi } => {
319                let half_theta = theta / 2.0;
320                let cos_half = half_theta.cos();
321                let sin_half = half_theta.sin();
322                let mut new_state = Array1::zeros(joint_state.len());
323                for i in 0..joint_state.len() {
324                    let bit = (i >> player_index) & 1;
325                    let flipped_i = i ^ (1 << player_index);
326                    if bit == 0 {
327                        new_state[i] += cos_half * joint_state[i];
328                        new_state[flipped_i] +=
329                            sin_half * Complex64::new(phi.cos(), phi.sin()) * joint_state[i];
330                    }
331                }
332                joint_state = new_state;
333            }
334            QuantumStrategy::Entangled => {}
335            QuantumStrategy::Mixed(_strategies) => {
336                let mut new_state = joint_state.clone();
337                for i in 0..new_state.len() {
338                    new_state[i] *= Complex64::new(1.0 / 2.0_f64.sqrt(), 0.0);
339                }
340                joint_state = new_state;
341            }
342        }
343        Ok(joint_state)
344    }
345    /// Measure the final game state and determine outcome
346    fn measure_game_outcome(&self, joint_state: Array1<Complex64>) -> QuantRS2Result<GameOutcome> {
347        let num_outcomes = joint_state.len();
348        let mut probabilities = Array1::zeros(num_outcomes);
349        for i in 0..num_outcomes {
350            probabilities[i] = joint_state[i].norm_sqr();
351        }
352        let outcome_index = Self::sample_outcome(&probabilities)?;
353        let mut classical_outcomes = Vec::new();
354        for player_idx in 0..self.num_players {
355            let bit = (outcome_index >> player_idx) & 1;
356            classical_outcomes.push(bit);
357        }
358        let payoffs = self.compute_payoffs(&classical_outcomes)?;
359        let is_nash = self.is_nash_equilibrium(&classical_outcomes, &payoffs)?;
360        Ok(GameOutcome {
361            classical_outcomes,
362            probabilities,
363            payoff_matrix: Some(payoffs),
364            is_nash_equilibrium: is_nash,
365        })
366    }
367    /// Sample an outcome based on probability distribution
368    fn sample_outcome(probabilities: &Array1<f64>) -> QuantRS2Result<usize> {
369        let mut rng = thread_rng();
370        use scirs2_core::random::prelude::*;
371        let random_value: f64 = rng.random();
372        let mut cumulative = 0.0;
373        for (i, &prob) in probabilities.iter().enumerate() {
374            cumulative += prob;
375            if random_value <= cumulative {
376                return Ok(i);
377            }
378        }
379        Ok(probabilities.len() - 1)
380    }
381    /// Compute payoffs for all players given classical outcomes
382    fn compute_payoffs(&self, outcomes: &[usize]) -> QuantRS2Result<Vec<f64>> {
383        let mut payoffs = Vec::new();
384        for (player_idx, payoff_matrix) in self.payoff_matrices.iter().enumerate() {
385            if outcomes.len() < 2 {
386                return Err(QuantRS2Error::InvalidInput(
387                    "Need at least 2 players for payoff calculation".to_string(),
388                ));
389            }
390            let player_action = outcomes[player_idx];
391            let opponent_action = outcomes[1 - player_idx % 2];
392            let payoff = payoff_matrix[[player_action, opponent_action]];
393            payoffs.push(payoff);
394        }
395        Ok(payoffs)
396    }
397    /// Check if current outcome is a Nash equilibrium
398    fn is_nash_equilibrium(&self, outcomes: &[usize], _payoffs: &[f64]) -> QuantRS2Result<bool> {
399        match self.game_type {
400            GameType::QuantumPrisonersDilemma => Ok(outcomes == &[1, 1]),
401            GameType::QuantumCoordination => Ok(outcomes[0] == outcomes[1]),
402            _ => Ok(false),
403        }
404    }
405    /// Find quantum Nash equilibria using iterative algorithm
406    pub fn find_quantum_nash_equilibria(&mut self) -> QuantRS2Result<Vec<GameOutcome>> {
407        let mut equilibria = Vec::new();
408        let theta_steps = 10;
409        let phi_steps = 10;
410        for i in 0..theta_steps {
411            for j in 0..phi_steps {
412                let theta = (i as f64) * std::f64::consts::PI / (theta_steps as f64);
413                let phi = (j as f64) * 2.0 * std::f64::consts::PI / (phi_steps as f64);
414                for player in &mut self.players {
415                    player.strategy = QuantumStrategy::Superposition { theta, phi };
416                }
417                let outcome = self.play_game()?;
418                if outcome.is_nash_equilibrium {
419                    equilibria.push(outcome);
420                }
421            }
422        }
423        Ok(equilibria)
424    }
425    /// Compute quantum advantage over classical game
426    pub fn quantum_advantage(&mut self) -> QuantRS2Result<f64> {
427        let quantum_outcome = self.play_game()?;
428        let quantum_payoffs = quantum_outcome.payoff_matrix.unwrap_or_default();
429        let quantum_total = quantum_payoffs.iter().sum::<f64>();
430        let classical_total = self.compute_classical_nash_payoff()?;
431        Ok(quantum_total - classical_total)
432    }
433    /// Compute payoff at classical Nash equilibrium
434    const fn compute_classical_nash_payoff(&self) -> QuantRS2Result<f64> {
435        match self.game_type {
436            GameType::QuantumPrisonersDilemma => Ok(2.0),
437            GameType::QuantumCoordination | _ => Ok(0.0),
438        }
439    }
440}
441/// Quantum mechanism design for multi-player games
442#[derive(Debug, Clone)]
443pub struct QuantumMechanism {
444    /// Number of players
445    pub num_players: usize,
446    /// Mechanism type
447    pub mechanism_type: String,
448    /// Quantum circuit implementing the mechanism
449    pub circuit: Option<Array2<Complex64>>,
450    /// Revenue function for the mechanism designer
451    pub revenue_function: Option<fn(&[f64]) -> f64>,
452}
453impl QuantumMechanism {
454    /// Create quantum auction mechanism
455    pub fn quantum_auction_mechanism(num_bidders: usize) -> Self {
456        Self {
457            num_players: num_bidders,
458            mechanism_type: "Quantum Auction".to_string(),
459            circuit: None,
460            revenue_function: Some(|bids| bids.iter().sum::<f64>() * 0.1),
461        }
462    }
463    /// Create quantum voting mechanism
464    pub fn quantum_voting_mechanism(num_voters: usize) -> Self {
465        Self {
466            num_players: num_voters,
467            mechanism_type: "Quantum Voting".to_string(),
468            circuit: None,
469            revenue_function: None,
470        }
471    }
472    /// Design optimal quantum mechanism
473    pub fn design_optimal_mechanism(&mut self) -> QuantRS2Result<Array2<Complex64>> {
474        let dim = 1 << self.num_players;
475        Ok(Array2::eye(dim))
476    }
477    /// Verify mechanism properties (incentive compatibility, individual rationality)
478    pub const fn verify_mechanism_properties(&self) -> QuantRS2Result<(bool, bool)> {
479        let incentive_compatible = true;
480        let individually_rational = true;
481        Ok((incentive_compatible, individually_rational))
482    }
483}
484#[cfg(test)]
485mod tests {
486    use super::*;
487    #[test]
488    fn test_quantum_player_creation() {
489        let strategy = QuantumStrategy::Superposition {
490            theta: std::f64::consts::PI / 4.0,
491            phi: 0.0,
492        };
493        let player = QuantumPlayer::new(0, strategy.clone());
494        assert_eq!(player.id, 0);
495        assert_eq!(player.strategy, strategy);
496        assert!(player.state.is_none());
497    }
498    #[test]
499    fn test_quantum_state_preparation() {
500        let mut player =
501            QuantumPlayer::new(0, QuantumStrategy::Classical(std::f64::consts::PI / 4.0));
502        let state = player
503            .prepare_quantum_state()
504            .expect("failed to prepare quantum state");
505        assert_eq!(state.len(), 2);
506        assert!(state[0].norm() > 0.0);
507        assert!(player.state.is_some());
508    }
509    #[test]
510    fn test_quantum_prisoners_dilemma() {
511        let game = QuantumGame::quantum_prisoners_dilemma()
512            .expect("failed to create quantum prisoners dilemma");
513        assert_eq!(game.game_type, GameType::QuantumPrisonersDilemma);
514        assert_eq!(game.num_players, 2);
515        assert_eq!(game.payoff_matrices.len(), 2);
516        assert!(game.entanglement_operator.is_some());
517    }
518    #[test]
519    fn test_quantum_coordination_game() {
520        let game = QuantumGame::quantum_coordination_game()
521            .expect("failed to create quantum coordination game");
522        assert_eq!(game.game_type, GameType::QuantumCoordination);
523        assert_eq!(game.num_players, 2);
524        assert_eq!(game.payoff_matrices.len(), 2);
525    }
526    #[test]
527    fn test_game_with_players() {
528        let mut game = QuantumGame::quantum_prisoners_dilemma()
529            .expect("failed to create quantum prisoners dilemma");
530        let player1 = QuantumPlayer::new(0, QuantumStrategy::Classical(0.0));
531        let player2 = QuantumPlayer::new(1, QuantumStrategy::Classical(std::f64::consts::PI));
532        game.add_player(player1).expect("failed to add player1");
533        game.add_player(player2).expect("failed to add player2");
534        let outcome = game.play_game().expect("failed to play game");
535        assert_eq!(outcome.classical_outcomes.len(), 2);
536        assert!(!outcome.probabilities.is_empty());
537    }
538    #[test]
539    fn test_entanglement_operator() {
540        let entanglement = QuantumGame::create_entanglement_operator(std::f64::consts::PI / 2.0)
541            .expect("failed to create entanglement operator");
542        assert_eq!(entanglement.dim(), (4, 4));
543        let conjugate_transpose = entanglement.t().mapv(|x| x.conj());
544        let product = conjugate_transpose.dot(&entanglement);
545        for i in 0..4 {
546            for j in 0..4 {
547                let expected = if i == j { 1.0 } else { 0.0 };
548                assert!((product[[i, j]].norm() - expected).abs() < 1e-10);
549            }
550        }
551    }
552    #[test]
553    fn test_nash_equilibrium_detection() {
554        let game = QuantumGame::quantum_prisoners_dilemma()
555            .expect("failed to create quantum prisoners dilemma");
556        let is_nash = game
557            .is_nash_equilibrium(&[1, 1], &[1.0, 1.0])
558            .expect("failed to check Nash equilibrium");
559        assert!(is_nash);
560        let is_nash = game
561            .is_nash_equilibrium(&[0, 0], &[3.0, 3.0])
562            .expect("failed to check Nash equilibrium");
563        assert!(!is_nash);
564    }
565    #[test]
566    fn test_quantum_mechanism_creation() {
567        let mechanism = QuantumMechanism::quantum_auction_mechanism(3);
568        assert_eq!(mechanism.num_players, 3);
569        assert_eq!(mechanism.mechanism_type, "Quantum Auction");
570        assert!(mechanism.revenue_function.is_some());
571    }
572    #[test]
573    fn test_mechanism_verification() {
574        let mechanism = QuantumMechanism::quantum_voting_mechanism(5);
575        let (ic, ir) = mechanism
576            .verify_mechanism_properties()
577            .expect("failed to verify mechanism properties");
578        assert!(ic);
579        assert!(ir);
580    }
581    #[test]
582    fn test_quantum_advantage_calculation() {
583        let mut game = QuantumGame::quantum_prisoners_dilemma()
584            .expect("failed to create quantum prisoners dilemma");
585        let player1 = QuantumPlayer::new(
586            0,
587            QuantumStrategy::Superposition {
588                theta: std::f64::consts::PI / 4.0,
589                phi: 0.0,
590            },
591        );
592        let player2 = QuantumPlayer::new(
593            1,
594            QuantumStrategy::Superposition {
595                theta: std::f64::consts::PI / 4.0,
596                phi: 0.0,
597            },
598        );
599        game.add_player(player1).expect("failed to add player1");
600        game.add_player(player2).expect("failed to add player2");
601        let advantage = game
602            .quantum_advantage()
603            .expect("failed to calculate quantum advantage");
604        assert!(advantage.is_finite());
605    }
606}