배틀쉽 간단 버전
이 Compact 컨트랙트는 배틀쉽 게임의 간단한 버전을 구현합니다. 함선은 숫자로, 게임 보드는 단일 숫자 라인으로 표현됩니다. 이 컨트랙트에서 다루는 기능은 다음과 같습니다:
- 명시적 상태 관리
- 비공개 상태 데이터 검증
- circuit 접근 제어
List연산- 중간 witness 기능
pragma language_version 0.22;
import CompactStandardLibrary;
export enum BoardState { UNSET, SET }
export enum ShotState { MISS, HIT }
export enum TurnState {
PLAYER_1_SHOOT,
PLAYER_1_CHECK,
PLAYER_2_SHOOT,
PLAYER_2_CHECK,
}
export enum WinState {
CONTINUE_PLAY,
PLAYER_1_WINS,
PLAYER_2_WINS
}
export sealed ledger player1: Bytes<32>;
export ledger player2: Bytes<32>;
export ledger turn: TurnState;
export ledger board1: Set<Bytes<32>>;// 선형 보드 형태
export ledger board2: Set<Bytes<32>>;// 함선 위치의 해시 저장소
export ledger board1State: BoardState;
export ledger board2State: BoardState;
export ledger player1Shot: List<Uint<8>>;// 현재 사격
export ledger player2Shot: List<Uint<8>>;
export ledger board1Hits: Set<Uint<8>>;// 이후 검증을 위해 저장된 이전 명중
export ledger board2Hits: Set<Uint<8>>;
export ledger winState: WinState;
export ledger board1HitCount: Counter;
export ledger board2HitCount: Counter;
witness localSetBoard(_x1: Uint<8>, _x2: Uint<8>) : BoardState;
witness localCheckBoard(x: Uint<8>) : ShotState;
witness localSk(): Bytes<32>;
constructor(_x1: Uint<8>, _x2: Uint<8>, _sk: Bytes<32>) {
assert(_x1 != _x2, "Cannot use the same number twice");
assert(_x1 > 0 && _x2 > 0, "No zero index, board starts at 1");
assert(_x1 <= 20 && _x2 <= 20, "Out of bounds, please keep ships on the board");
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
player1 = disclose(pubKey);
// 나중에 검증하기 위해 입력값을 해시합니다. 사용자는 동일한 값과 _sk를 제공해야 합니다
const hash1 = commitBoardSpace(_x1 as Bytes<32>, _sk);
board1.insert(hash1);
const hash2 = commitBoardSpace(_x2 as Bytes<32>, _sk);
board1.insert(hash2);
// 모범 사례 예시 -- disclose(localSetBoard1(_x1, _x2))를 사용하지 마세요,
// 필요한 것만 공개하세요 (localBoardState);
const localBoardState = localSetBoard(_x1, _x2);
assert(localBoardState == BoardState.SET, "Please update the state of board1 to SET");
board1State = disclose(localBoardState);
board2State = BoardState.UNSET;
winState = WinState.CONTINUE_PLAY;
}
export circuit acceptGame(_x1: Uint<8>, _x2: Uint<8>) : [] {
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
assert(player1 != disclose(pubKey), "You cannot play against yourself");
assert(board2State == BoardState.UNSET, "There is already a player2");
assert(_x1 != _x2, "Cannot use the same number twice");
assert(_x1 > 0 && _x2 > 0, "No zero index, please keep ships on the board");
assert(_x1 <= 20 && _x2 <= 20, "Out of bounds, please keep ships on the board");
player2 = disclose(pubKey);
// 입력값을 해시하고 나중에 비교하기 위해 ledger에 저장합니다
const hash1 = commitBoardSpace(_x1 as Bytes<32>, _sk);
board2.insert(hash1);
const hash2 = commitBoardSpace(_x2 as Bytes<32>, _sk);
board2.insert(hash2);
const localBoardState = localSetBoard(_x1, _x2);
assert(localBoardState == BoardState.SET, "Please update the state of your board to SET");
board2State = disclose(localBoardState);
turn = TurnState.PLAYER_1_SHOOT;
}
export circuit player1Shoot (x: Uint<8>): [] {
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
assert(player1 == disclose(pubKey), "You are not player1");
assert(board2State == BoardState.SET, "Player 2 has not yet set their board");
assert(turn == TurnState.PLAYER_1_SHOOT, "It is not player1 turn to shoot");
assert(winState == WinState.CONTINUE_PLAY, "A winner has already been declared");
assert(x > 0 && x <= 20, "Shot out of bounds, please shoot on the board");
// 사격은 공개 정보입니다
const currentShot = disclose(x);
assert(!board2Hits.member(currentShot), "Cheat Detected: Player1: Attempt to repeat a previous HIT");
player1Shot.pushFront(currentShot);
turn = TurnState.PLAYER_2_CHECK;
}
export circuit player2Shoot(x: Uint<8>): [] {
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
assert(player2 == disclose(pubKey), "You are not player2");
assert(turn == TurnState.PLAYER_2_SHOOT, "It is not player2 turn to shoot");
assert(winState == WinState.CONTINUE_PLAY, "A winner has already been declared");
assert(x > 0 && x <= 20, "Shot out of bounds, please shoot on the board");
// 사격은 공개 정보입니다
const currentShot = disclose(x);
assert(!board1Hits.member(currentShot), "Cheat Detected: Player2: Attempt to repeat a previous HIT");
player2Shot.pushFront(currentShot);
turn = TurnState.PLAYER_1_CHECK;
}
export circuit checkBoard1 () : [] {
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
assert(player1 == disclose(pubKey), "You are not player1");
assert(winState == WinState.CONTINUE_PLAY, "A winner has already been declared");
assert(turn == TurnState.PLAYER_1_CHECK, "It is not Player 1 turn to CHECK");
assert(!player2Shot.isEmpty(), "No shot to check");
const currentShot = player2Shot.head().value;
assert(!board1Hits.member(currentShot), "Cheat Detected: Player2: Attempt to repeat a previous HIT");
player2Shot.popFront();
const honestyCheckHash = commitBoardSpace(currentShot as Bytes<32>, _sk);
// currentShot은 이미 공개되었지만, 여기서도 컴파일러를 만족시켜야 합니다
const shotState = disclose(localCheckBoard(currentShot));
assert(shotState == ShotState.HIT || shotState == ShotState.MISS, "Please provide a valid state");
if(shotState == ShotState.MISS){
// 신뢰하지 말고 검증하세요
assert(!board1.member(honestyCheckHash), "Cheat Detected: Player 1: claimed a MISS, when it was in fact a HIT");
turn = TurnState.PLAYER_1_SHOOT;
} else {
// 신뢰하지 말고 검증하세요
assert(board1.member(honestyCheckHash), "Cheat Detected: Player 2: claimed a HIT, when it was in fact a MISS. Why would they do that?");
board1HitCount.increment(1);
board1Hits.insert(currentShot);
turn = TurnState.PLAYER_1_SHOOT;
// 누군가 이겼나요?
winState = board1HitCount == 2 ? WinState.PLAYER_2_WINS : WinState.CONTINUE_PLAY;
}
}
export circuit checkBoard2 () : [] {
const _sk = localSk();
const pubKey = getDappPubKey(_sk);
assert(player2 == disclose(pubKey), "You are not player2");
assert(board2State == BoardState.SET, "Player 2 has not set the board yet");
assert(winState == WinState.CONTINUE_PLAY, "A winner has already been declared");
assert(turn == TurnState.PLAYER_2_CHECK, "It is not Player 2 turn to CHECK");
assert(!player1Shot.isEmpty(), "No shot to check");
const currentShot = player1Shot.head().value;
assert(!board2Hits.member(currentShot), "Cheat Detected: Player 1: Attempt to repeat a previous HIT");
player1Shot.popFront();
const honestyCheckHash = commitBoardSpace(currentShot as Bytes<32>, _sk);
const shotState = disclose(localCheckBoard(currentShot));
assert(shotState == ShotState.HIT || shotState == ShotState.MISS, "Please provide a valid state");
if(shotState == ShotState.MISS){
// 신뢰하지 말고 검증하세요
assert(!board2.member(honestyCheckHash), "Cheat Detected: Player 2: claimed a MISS, when it was in fact a HIT");
turn = TurnState.PLAYER_2_SHOOT;
} else {
// 신뢰하지 말고 검증하세요
assert(board2.member(honestyCheckHash), "Cheat Detected: Player 2: claimed a HIT, when is was in fact a MISS. Why would they do that?");
board2HitCount.increment(1);
board2Hits.insert(currentShot);
turn = TurnState.PLAYER_2_SHOOT;
// 누군가 이겼나요?
winState = board2HitCount == 2 ? WinState.PLAYER_1_WINS : WinState.CONTINUE_PLAY;
}
}
circuit commitBoardSpace(_x: Bytes<32>, _sk: Bytes<32>) : Bytes<32> {
const hash = persistentHash<Vector<2, Bytes<32>>>([_x, _sk]);
return disclose(hash);
}
export circuit getDappPubKey(_sk: Bytes<32>): Bytes<32> {
return persistentHash<Vector<2, Bytes<32>>>([pad(32, "battleship:pk:"), _sk]);
}