솔리디티 연습 - 가위바위보 게임

0xWonTiger·2022년 7월 14일
0

솔리디티를 사용해 간단한 가위바위보 컨트랙트를 짜보는 실습

  1. 사용자와 게임 구조체 생성
    • 컨트랙트 틀 짜기
    • 플레이어 struct 생성
    • 게임 struct 생성
  2. 게임 생성하기
    • 게임을 생성
    • 방 번호를 지정 (추가 생성시 방번호 +1)
  3. 방 참가하기
    • 참가자 / 방 번호 / 가위바위보 값 인자 / 베팅금액 설정
    • 게임결과 업데이트
  4. 방별 베팅된 금액을 확인하기
  5. 게임 종료 후 베팅금액 송금
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract RPS {
    constructor () payable {} // 송금이 가능하다는 것을 명시

    enum Hand {
        rock,
        paper,
        scissors
    }

    enum PlayerStatus {
        STATUS_WIN,
        STATUS_LOSE,
        STATUS_TIE,
        STATUS_PENDING
    }

    enum GameStatus {
        STATUS_NOT_STARTED,
        STATUS_STARTED,
        STATUS_COMPLETE,
        STATUS_ERROR
    }

    struct Player {
        address payable addr; // 주소
        uint256 playerBetAmount; // 베팅 금액
        Hand hand; // 플레이어의 가위바위보값
        PlayerStatus playerStatus; // 사용자 상태
    }

    struct Game {
        Player originator; // 방장 정보
        Player taker; // 참여자 정보
        uint256 betAmount; // 총 베팅 금액
        GameStatus gameStatus; // 게임의 현 상태
    }
    mapping(uint => Game) rooms; // rooms[0], rooms[1]로 접근
    uint roomLen = 0; // rooms의 키값으로 방 생성마다 1씩 올라간다

    modifier isValidHand (Hand _hand) {
        require((_hand == Hand.rock) || (_hand == Hand.paper) || (_hand == Hand.scissors));
        _;
    }

    function createRoom (Hand _hand) public payable isValidHand(_hand) returns(uint roomNum) {
        rooms[roomLen] = Game({
            betAmount: msg.value,
            gameStatus: GameStatus.STATUS_NOT_STARTED,
            originator: Player({
                addr: payable(msg.sender),
                playerBetAmount: msg.value,
                hand: _hand,
                playerStatus: PlayerStatus.STATUS_PENDING
            }),
            taker: Player({
                addr: payable(msg.sender),
                playerBetAmount: 0,
                hand: Hand.rock,
                playerStatus: PlayerStatus.STATUS_PENDING
            })
        });
        roomNum = roomLen; // 현재 방 번호를 roomNum에 넣고 리턴
        roomLen = roomLen+1; // 다음 방 번호에 1추가
    }
    
    function joinRoom (uint roomNum, Hand _hand) public payable isValidHand(_hand) {
        rooms[roomLen].taker = Player({
            hand: _hand,
            addr: payable(msg.sender),
            playerStatus: PlayerStatus.STATUS_PENDING,
            playerBetAmount: msg.value
        });
        rooms[roomNum].betAmount = rooms[roomNum].betAmount + msg.value;
        compareHands(roomNum);
    }

    function compareHands(uint roomNum) private {
        uint8 originator = uint8(rooms[roomNum].originator.hand);
        uint8 taker = uint8(rooms[roomNum].taker.hand);

        rooms[roomNum].gameStatus = GameStatus.STATUS_STARTED;
        
        if (taker == originator) {
            rooms[roomNum].originator.playerStatus = PlayerStatus.STATUS_TIE;
            rooms[roomNum].taker.playerStatus = PlayerStatus.STATUS_TIE;
        }
        else if ((taker+1)%3 == originator) {
            rooms[roomNum].originator.playerStatus = PlayerStatus.STATUS_WIN;
            rooms[roomNum].taker.playerStatus = PlayerStatus.STATUS_LOSE;
        }
        else if ((originator+1)%3 == taker) {
            rooms[roomNum].originator.playerStatus = PlayerStatus.STATUS_LOSE;
            rooms[roomNum].taker.playerStatus = PlayerStatus.STATUS_WIN;
        } 
        else {
            rooms[roomNum].gameStatus = GameStatus.STATUS_ERROR;
        }
    }

    function checkTotalPay(uint roomNum) public view returns(uint roomNumPay) {
        return rooms[roomNum].betAmount;
    }

    modifier isPlayer (uint roomNum, address sender) {
        require(sender == rooms[roomNum].originator.addr || sender == rooms[roomNum].taker.addr);
        _;
    }

    function payout(uint roomNum) public payable isPlayer(roomNum, msg.sender) {
        if (rooms[roomNum].originator.playerStatus == PlayerStatus.STATUS_TIE && rooms[roomNum].taker.playerStatus == PlayerStatus.STATUS_TIE) {
            rooms[roomNum].originator.addr.transfer(rooms[roomNum].originator.playerBetAmount);
            rooms[roomNum].taker.addr.transfer(rooms[roomNum].taker.playerBetAmount);
        }
        else {
            if (rooms[roomNum].originator.playerStatus == PlayerStatus.STATUS_WIN) {
                rooms[roomNum].originator.addr.transfer(rooms[roomNum].betAmount);
            } 
            else if (rooms[roomNum].taker.playerStatus == PlayerStatus.STATUS_WIN) {
                rooms[roomNum].taker.addr.transfer(rooms[roomNum].betAmount);
            }
            else {
                rooms[roomNum].originator.addr.transfer(rooms[roomNum].originator.playerBetAmount);
                rooms[roomNum].taker.addr.transfer(rooms[roomNum].taker.playerBetAmount);
            }
        }
        rooms[roomNum].gameStatus = GameStatus.STATUS_COMPLETE; // 게임 종료
    }
}
profile
Blockchain & Crypto Enthusiast

0개의 댓글