arnaucube
arnaucube/phantom-zonePublic Self-hosted repository
phantom-zone/examples/bomberman.rs
examples/bomberman.rsRaw
  1use std::fmt::Debug;
  2
  3use bin_rs::*;
  4use itertools::Itertools;
  5use rand::{thread_rng, Rng, RngCore};
  6
  7struct Coordinates<T>(T, T);
  8impl<T> Coordinates<T> {
  9    fn new(x: T, y: T) -> Self {
 10        Coordinates(x, y)
 11    }
 12    fn x(&self) -> &T {
 13        &self.0
 14    }
 15
 16    fn y(&self) -> &T {
 17        &self.1
 18    }
 19}
 20
 21impl<T> Debug for Coordinates<T>
 22where
 23    T: Debug,
 24{
 25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 26        f.debug_struct("Coordinates")
 27            .field("x", self.x())
 28            .field("y", self.y())
 29            .finish()
 30    }
 31}
 32
 33fn coordinates_is_equal(a: &Coordinates<FheUint8>, b: &Coordinates<FheUint8>) -> FheBool {
 34    &(a.x().eq(b.x())) & &(a.y().eq(b.y()))
 35}
 36
 37fn traverse_map(p0: &[Coordinates<FheUint8>], bomb_coords: &[Coordinates<FheUint8>]) -> FheBool {
 38    // First move
 39    let mut out = coordinates_is_equal(&p0[0], &bomb_coords[0]);
 40    bomb_coords.iter().skip(1).for_each(|b_coord| {
 41        out |= coordinates_is_equal(&p0[0], &b_coord);
 42    });
 43
 44    // rest of the moves
 45    p0.iter().skip(1).for_each(|m_coord| {
 46        bomb_coords.iter().for_each(|b_coord| {
 47            out |= coordinates_is_equal(m_coord, b_coord);
 48        });
 49    });
 50
 51    out
 52}
 53
 54// Do you recall bomberman? It's an interesting game where the bomberman has to
 55// cross the map without stepping on strategically placed bombs all across the
 56// map. Below we implement a very basic prototype of bomberman with 4 players.
 57//
 58// The map has 256 tiles with bottom left-most tile labelled with coordinate
 59// (0,0) and top right-most tile labelled with coordinate (255, 255). There are
 60// 4 players: Player 0, Player 1, Player 2, Player 3. Player 0's task is to walk
 61// across the map with fixed no. of moves while preventing itself from stepping
 62// on any of the bombs placed across the map by Player 1, 2, and 3.
 63//
 64// The twist is that Player 0's moves and the locations of bombs placed by other
 65// players are encrypted. Player 0 moves across the map in encrypted domain.
 66// Only a boolean output indicating whether player 0 survived after all the
 67// moves or killed itself by stepping onto a bomb is revealed at the end. If
 68// player 0 survives, Player 1, 2, 3 never learn what moves did it make. If
 69// player 0 kills itself by stepping onto a bomb, it only learns that bomb was
 70// placed on one coordinates it moved to. Moreover, Player 1, 2, 3 never learn
 71// about locations of each other bombs or even whose bomb killed Player 1.
 72fn main() {
 73    set_parameter_set(ParameterSelector::NonInteractiveLTE4Party);
 74
 75    // set application's common reference seed
 76    let mut seed = [0; 32];
 77    thread_rng().fill_bytes(&mut seed);
 78    set_common_reference_seed(seed);
 79
 80    let no_of_parties = 4;
 81
 82    // Client side //
 83
 84    let cks = (0..no_of_parties).map(|_| gen_client_key()).collect_vec();
 85
 86    let server_key_shares = cks
 87        .iter()
 88        .enumerate()
 89        .map(|(index, k)| gen_server_key_share(index, no_of_parties, k))
 90        .collect_vec();
 91
 92    // encrypt inputs
 93    let no_of_moves = 10;
 94    let player_0_moves = (0..no_of_moves)
 95        .map(|_| Coordinates::new(thread_rng().gen::<u8>(), thread_rng().gen()))
 96        .collect_vec();
 97    let player_1_bomb = Coordinates::new(thread_rng().gen::<u8>(), thread_rng().gen());
 98    let player_2_bomb = Coordinates::new(thread_rng().gen::<u8>(), thread_rng().gen());
 99    let player_3_bomb = Coordinates::new(thread_rng().gen::<u8>(), thread_rng().gen());
100
101    println!("P0 move coordinates: {:?}", &player_0_moves);
102    println!("P1 bomb coordinate : {:?}", &player_1_bomb);
103    println!("P2 bomb coordinate : {:?}", &player_2_bomb);
104    println!("P3 bomb coordinate : {:?}", &player_3_bomb);
105
106    // Al playes encrypt their private inputs
107    let player_0_enc = cks[0].encrypt(
108        player_0_moves
109            .iter()
110            .flat_map(|c| vec![*c.x(), *c.y()])
111            .collect_vec()
112            .as_slice(),
113    );
114    let player_1_enc = cks[1].encrypt(vec![*player_1_bomb.x(), *player_1_bomb.y()].as_slice());
115    let player_2_enc = cks[2].encrypt(vec![*player_2_bomb.x(), *player_2_bomb.y()].as_slice());
116    let player_3_enc = cks[3].encrypt(vec![*player_3_bomb.x(), *player_3_bomb.y()].as_slice());
117
118    // All player upload the encrypted inputs and server key shates to the server
119
120    // Server side //
121
122    let server_key = aggregate_server_key_shares(&server_key_shares);
123    server_key.set_server_key();
124
125    // server parses all player inputs
126    let player_0_moves_enc = {
127        let c = player_0_enc.unseed::<Vec<Vec<u64>>>().key_switch(0);
128        (0..no_of_moves)
129            .map(|i| Coordinates::new(c.extract_at(2 * i), c.extract_at(i * 2 + 1)))
130            .collect_vec()
131    };
132    let player_1_bomb_enc = {
133        let c = player_1_enc.unseed::<Vec<Vec<u64>>>().key_switch(1);
134        Coordinates::new(c.extract_at(0), c.extract_at(1))
135    };
136    let player_2_bomb_enc = {
137        let c = player_2_enc.unseed::<Vec<Vec<u64>>>().key_switch(2);
138        Coordinates::new(c.extract_at(0), c.extract_at(1))
139    };
140    let player_3_bomb_enc = {
141        let c = player_3_enc.unseed::<Vec<Vec<u64>>>().key_switch(3);
142        Coordinates::new(c.extract_at(0), c.extract_at(1))
143    };
144
145    // run the game
146    let player_0_dead_ct = traverse_map(
147        &player_0_moves_enc,
148        &vec![player_1_bomb_enc, player_2_bomb_enc, player_3_bomb_enc],
149    );
150
151    // All players generate decryption shares
152    let decryption_shares = cks
153        .iter()
154        .map(|k| k.gen_decryption_share(&player_0_dead_ct))
155        .collect_vec();
156    let player_0_dead = cks[0].aggregate_decryption_shares(&player_0_dead_ct, &decryption_shares);
157
158    if player_0_dead {
159        println!("Oops! Player 0 dead");
160    } else {
161        println!("Wohoo! Player 0 survived");
162    }
163}