Skip to content

Commit

Permalink
Add circuit for board validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Shigoto-dev19 committed Feb 6, 2024
1 parent 37365f3 commit 0d7e04a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 41 deletions.
19 changes: 8 additions & 11 deletions src/Battleships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
method,
SmartContract,
Struct,
Poseidon,
} from 'o1js';
import { BoardCircuit } from './client';

export { Battleships }

Expand Down Expand Up @@ -40,24 +40,18 @@ class Battleships extends SmartContract {
}

@method hostGame(serializedBoard1: Field) {
const boardHash = Poseidon.hash([serializedBoard1]);
//TODO deserializeBoard(serializedBoard1);
//TODO validateBoard();

const boardHash1 = BoardCircuit.validateBoard(serializedBoard1);
this.player1.set({
address: this.sender,
boardHash,
boardHash: boardHash1,
});
}

@method joinGame(serializedBoard2: Field) {
const boardHash = Poseidon.hash([serializedBoard2]);
//TODO deserializeBoard(serializedBoard1);
//TODO validateBoard();

const boardHash2 = BoardCircuit.validateBoard(serializedBoard2);
this.player2.set({
address: this.sender,
boardHash
boardHash: boardHash2,
});

this.joinable.set(Bool(false));
Expand All @@ -68,4 +62,7 @@ class Battleships extends SmartContract {
@method finalizeGame() { return }
}

//TODO Test Board Circuit logic
//TODO Add attack circuit
//TODO Test attack circuit
//TODO Add player client class
103 changes: 73 additions & 30 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,27 @@ import {
Field,
Bool,
Poseidon,
Provable,
} from 'o1js';

export { Client }

class Client {
board: number[][];
boardHash?: Field = undefined;
serializedBoard: Field;

constructor(board: number[][]) {
this.board = board;
}
export { BoardUtils, BoardCircuit }

class BoardUtils {
//TODO refactor
serializeBoard() {
static serializeBoard(board: number[][]) {
let serializedCoordinates: Bool[][][] = [];
for (const shipCoordinates of this.board) {
for (const shipCoordinates of board) {
let x = Field(shipCoordinates[0]).toBits(8);
let y = Field(shipCoordinates[1]).toBits(8);
let z = Field(shipCoordinates[2]).toBits(8);
serializedCoordinates.push([x, y, z]);
}
// the serialized board is 24 * 5 = 120 bit-field
this.serializedBoard = Field.fromBits(serializedCoordinates.flat().flat())
const serializedBoard = Field.fromBits(serializedCoordinates.flat().flat());
return serializedBoard
}

deserializeBoard() {
static deserializeBoard(serializedBoard: Field) {
const splitArrayIntoGroups= <T>(array: T[], groupSize: number): T[][] => {
let result: T[][] = [];
for (let i = 0; i < array.length; i += groupSize) {
Expand All @@ -37,41 +31,90 @@ class Client {
return result
}

let bits = this.serializedBoard.toBits(120);
let bits = serializedBoard.toBits(120);
let serializedCoordinates = splitArrayIntoGroups(bits, 8).map(f => Field.fromBits(f));
let board = splitArrayIntoGroups(serializedCoordinates, 3);

return board
}

hashBoard() {
this.boardHash = this.boardHash ?? Poseidon.hash([this.serializedBoard]);
static hashBoard(board: Field[][]) {
return Poseidon.hash(board.flat());
}
}

class BoardCircuit {
//TODO Add meaningful error logs
static validateShipInRange(ship: Field[], shipLength: number) {
// horizontal check: z=ship[2]=0
const checkHorizontal = () => {
ship[0].add(shipLength).assertLessThan(10);
ship[1].assertLessThan(10);
return Bool(true)
}

// vertical check: z=ship[2]=1
const checkVertical = () => {
ship[0].assertLessThan(10);
ship[1].add(shipLength).assertLessThan(10);
return Bool(true)
}

// verify z is binary
ship[2].assertLessThanOrEqual(1);

const isInRange = Provable.if(ship[2].equals(1), checkVertical(), checkHorizontal());
isInRange.assertTrue;
}

static placeShip = (ship: Field[], shipLength: number, boardMap: Field[]) => {
const increment = Provable.if(ship[2].equals(1), Field(10), Field(0));
let index = ship[0].add(ship[1].mul(10));
for(let i=0; i<shipLength; i++) {
let coordinate = index.add(i).add(increment);
let check = boardMap.some(item => item.equals(coordinate).toBoolean());
Provable.log('check: ', check)
Bool(check).assertFalse('Ship collision detected!');
boardMap.push(coordinate);
}
return boardMap
}

validateBoard() {
//TODO 1. deserialize board
//TODO 2. check ships non-collision
//TODO 3. check ships in board range
//TODO 4. assert board hash compliance
return
static validateShipsLocation(ships: Field[][]) {
const shipLength = [5, 4, 3, 3, 2];
let boardMap: Field[] = [];
for (let i = 0; i < 5; i++) {
// range check
BoardCircuit.validateShipInRange(ships[i], shipLength[i]);
// collision check
boardMap = BoardCircuit.placeShip(ships[i], shipLength[i], boardMap);
Provable.log('boardMap: ', boardMap);
}
}

static validateBoard(serializedBoard: Field) {
const board = BoardUtils.deserializeBoard(serializedBoard);
this.validateShipsLocation(board);
const boardHash = BoardUtils.hashBoard(board);

return boardHash
}
}

const player1Board = [
[0, 0, 0],
[4, 0, 0],
[0, 1, 0],
[0, 2, 0],
[0, 3, 0],
[0, 4, 0],
];

let player = new Client(player1Board);
player.serializeBoard();
let board = player.deserializeBoard().flat().map(x => Number(x.toBigInt()));
const serializedBoard = BoardUtils.serializeBoard(player1Board);
let deserializeBoard = BoardUtils.deserializeBoard(serializedBoard).flat().map(x => Number(x.toBigInt()));

console.log('initial board: ', player1Board.flat());
console.log('deserialized board: ', board);
console.log('deserialized board: ', deserializeBoard);

console.log('collision check: ', BoardCircuit.validateShipsLocation(player1Board.map(ship => ship.map(Field))))

//TODO Finsih board circuit
//TODO Update contract
//TODO Add board integration tests

0 comments on commit 0d7e04a

Please sign in to comment.