Skip to content

Instantly share code, notes, and snippets.

@qkdxorjs1002
Last active October 5, 2021 09:49
Show Gist options
  • Save qkdxorjs1002/2a1e8ad752308df3935de10c1feb0c7a to your computer and use it in GitHub Desktop.
Save qkdxorjs1002/2a1e8ad752308df3935de10c1feb0c7a to your computer and use it in GitHub Desktop.
Rock, Scissor, Paper with Solidity
pragma solidity ^0.5.1;
contract RSP {
/**
* 각 패의 2진 값
*
* 001: 바위 / 010: 가위 / 100: 보
*/
uint8 constant ROCK = 1;
uint8 constant SCISSOR = 2;
uint8 constant PAPER = 4;
/**
* 각 패 XOR 한 값으로 승패 여부 판단
*
* 000: 비길 경우
* 011: 바위가 이겼을 경우
* 110: 가위가 이겼을 경우
* 101: 보가 이겼을 경우
*/
uint8 constant WIN_ROCK = ROCK ^ SCISSOR;
uint8 constant WIN_SCISSOR = SCISSOR ^ PAPER;
uint8 constant WIN_PAPER = PAPER ^ ROCK;
/**
* 가위바위보 Room 구조체
*
* uint numberOfUser - Room에 참여한 유저 수
* (uint => User) userList - Room에 참여한 유저 목록
* uint betWei - Room 호스트가 설정한 베팅 기준 금액
* uint totalPaid - 지불된 전체 금액
*/
struct Room {
uint numberOfUser;
mapping (uint => User) userList;
uint betWei;
uint totalPaid;
}
/**
* 가위바위보 유저 구조체
*
* address(payable) userAddress - 유저 주소
* uint8 rsp - 유저가 제시한 가위바위보 패
*/
struct User {
address payable userAddress;
uint8 rsp;
}
// Room 목록
mapping (address => Room) private roomList;
/**
* 가위바위보 혼자 플레이
*
* @param _rsp 가위바위보 패 [rock, scissor, paper]
* @return string(memory) 승패 여부
*/
function playAlone(string memory _rsp) public returns(string memory) {
uint8[3] memory rsps = [ROCK, SCISSOR, PAPER];
uint8 rsp = stringToRSP(_rsp);
// 랜덤 패와 유저가 제시한 패 XOR
uint8 result = stringToRSP(_rsp) ^ rsps[uint(keccak256(abi.encodePacked(now, msg.sender, uint(1)))) % 3];
// WIN CASE 전부 비교
if (result == WIN_ROCK) {
if (rsp == ROCK) {
return "win";
} else {
return "lose";
}
}
if (result == WIN_SCISSOR) {
if (rsp == SCISSOR) {
return "win";
} else {
return "lose";
}
}
if (result == WIN_PAPER) {
if (rsp == PAPER) {
return "win";
} else {
return "lose";
}
}
return "draw";
}
/**
* 가위바위보 Room 생성
*
* @param _betEther Room 베팅 기준 금액
* @param _rsp 가위바위보 패 [rock, scissor, paper]
* @return address 생성된 Room 주소
*/
function createRoom(uint _betEther, string memory _rsp) public payable returns(address) {
// 기존에 생성된 Room이 있는지 확인
require(roomList[msg.sender].numberOfUser == 0,
"Room is already created.");
// 기준 베팅 금액이 0을 초과하는지 확인
require(_betEther > 0,
"\'_betEther\' must be bigger than 0.");
// 지불한 금액이 기준 베팅 금액과 일치하는지 확인
require(msg.value == (_betEther * 1 ether),
string(abi.encodePacked("You must be pay value that equal with ", _betEther)));
// 제시한 패가 올바른지 확인
require(compareStrings(_rsp, "rock") || compareStrings(_rsp, "scissor") || compareStrings(_rsp, "paper"),
"You must be set \'_rsp\' in \"rock\", \"scissor\" and \"paper\"");
// Room initialize
roomList[msg.sender].numberOfUser += 1;
roomList[msg.sender].userList[0].userAddress = msg.sender;
roomList[msg.sender].userList[0].rsp = stringToRSP(_rsp);
roomList[msg.sender].betWei = _betEther * 1 ether;
roomList[msg.sender].totalPaid += msg.value;
// Room 고유 번호 반환
return msg.sender;
}
/**
* 가위바위보 Room 참여
*
* @param _roomId Room ID
* @param _rsp 가위바위보 패 [rock, scissor, paper]
*/
function joinRoom(address _roomId, string memory _rsp) public payable returns(address, string memory) {
// 존재하는 Room인지 확인
require(roomList[_roomId].numberOfUser == 1,
"\'_roomId\' is not valid.");
// 지불한 금액이 기준 베팅 금액과 일치하는지 확인
require(msg.value == roomList[_roomId].betWei,
string(abi.encodePacked("You must be pay value that equal with ", roomList[_roomId].betWei)));
// 제시한 패가 올바른지 확인
require(compareStrings(_rsp, "rock") || compareStrings(_rsp, "scissor") || compareStrings(_rsp, "paper"),
"You must be set \'_rsp\' in \"rock\", \"scissor\" and \"paper\"");
// Room에 유저 추가
roomList[_roomId].numberOfUser += 1;
roomList[_roomId].userList[1].userAddress = msg.sender;
roomList[_roomId].userList[1].rsp = stringToRSP(_rsp);
roomList[_roomId].totalPaid += msg.value;
// 승패 여부 확인 후 결과 반환
return checkWinner(_roomId);
}
/**
* 가위바위보 Room 제거
*
* @param _roomId Room ID
*/
function removeRoom(address _roomId) public {
// 요청자가 Room 호스트인지 확인
require(roomList[_roomId].userList[0].userAddress == msg.sender,
"You are not room owner.");
if (!roomList[_roomId].userList[0].userAddress.send(roomList[_roomId].betWei)) {
revert();
} else {
delete roomList[_roomId];
}
}
/**
* 가위바위보 Room 확인
*
* @param _roomId Room ID
* @return (uint, uint, uint) Room 참여 유저 수, 기준 베팅 금액, 지불된 전체 금액
*/
function checkRoom(address _roomId) public returns(uint, uint, uint) {
// 존재하는 Room인지 확인
require(roomList[_roomId].numberOfUser > 0,
"Room is not exist.");
// Room 정보 반환
return (roomList[_roomId].numberOfUser,
roomList[_roomId].betWei,
roomList[_roomId].totalPaid);
}
/**
* 가위바위보 승자 확인 및 처리
*
* @param _roomId Room ID
* @return (address, string(memory)) 승리한 유저 주소, 승자가 제시한 패
*/
function checkWinner(address _roomId) private returns(address, string memory) {
Room storage room = roomList[_roomId];
// Room에 유저가 모두 참여했는지 확인
require(room.numberOfUser == 2,
"The competitor is not joined.");
User memory userA = room.userList[0];
User memory userB = room.userList[1];
// 유저 패 XOR
uint8 result = userA.rsp ^ userB.rsp;
User memory winner;
// WIN CASE 비교
if (result == WIN_ROCK) {
if (userA.rsp == ROCK) {
winner = userA;
}
if (userB.rsp == ROCK) {
winner = userB;
}
} else if (result == WIN_SCISSOR) {
if (userA.rsp == SCISSOR) {
winner = userA;
}
if (userB.rsp == SCISSOR) {
winner = userB;
}
} else if (result == WIN_PAPER) {
if (userA.rsp == PAPER) {
winner = userA;
}
if (userB.rsp == PAPER) {
winner = userB;
}
} else {
// 비겼을 경우 지불된 금액 모두 각 참여 유저에게 반환
if (!userA.userAddress.send(room.betWei) ||
!userB.userAddress.send(room.betWei)) {
revert("Error occured while refunding bet.");
}
delete roomList[_roomId];
return (address(0x0), "draw");
}
// 승자에게 지불된 베팅 금액 모두 전송
if (!winner.userAddress.send(roomList[_roomId].totalPaid)) {
revert("Error occured while send bet to winner.");
} else {
delete roomList[_roomId];
}
// 승자 주소와 제시한 패 반환
return (winner.userAddress, rspToString(winner.rsp));
}
/**
* 문자열 비교
*
* @param _a 비교대상 1
* @param _b 비교대상 2
* @return bool 비교 결과
*/
function compareStrings(string memory _a, string memory _b) private pure returns(bool) {
// 인코딩한 String 해싱하여 비교
return (keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)));
}
/**
* 문자열 패를 uint8 형식으로 변환
*
* @param _rsp 문자열 패
* @return uint8 결과
*/
function stringToRSP(string memory _rsp) private returns(uint8) {
if (compareStrings(_rsp, "rock")) {
return ROCK;
} else if (compareStrings(_rsp, "scissor")) {
return SCISSOR;
} else if (compareStrings(_rsp, "paper")) {
return PAPER;
} else {
return 0;
}
}
/**
* uint8 패를 문자열로 변환
*
* @param _rsp uint8 패
* @return string(memory) 결과
*/
function rspToString(uint8 _rsp) private returns(string memory) {
if (_rsp == ROCK) {
return "rock";
} else if (_rsp == SCISSOR) {
return "scissor";
} else if (_rsp == PAPER) {
return "paper";
} else {
return "";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment