크립토 좀비 1-4. 좀비 전투 시스템

2022. 3. 31. 14:28개발 잡부/블록체인

728x90
 

#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies

CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts

cryptozombies.io


1. Payable

payable은 이더를 받을 수 있게 하는 함수 유형이다.
함수를 실행하기 위해 사용료를 지불하는 것이다.

payable 표시가 없다면 이더를 보내려할 때 거부할 것이다.


이더리움 표시

ether또한 days나 weeks처럼 하나의 전역 단위로 동작한다.

1 ether

정답

...

  // 1. 여기에 levelUpFee를 정의하게
  uint levelUpFee = 0.001 ether;

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  // 2. 여기에 levelUp 함수를 삽입하게
  function levelUp(uint _zombieId) external payable {
    require(msg.value == levelUpFee);
    zombies[_zombieId].level++;
  }

...

2. 출금

그렇다면 이렇게 쌓인 돈은 어떻게 인출할 수 있을까?

address변수는 transfer라는 내장 함수를 가지고 있다.
돈을 보내는 코드인데,
owner(소유자, 나)에게 보내는 코드를 내부에 짤수도 있다.

contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }
}

쌓인 자산 보기

this.balance를 통해 컨트랙트에 저장된 이더의 양을 가져올 수 있다.


정답

...

  // 1. 여기에 withdraw 함수를 생성하게
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }

  // 2. 여기에 setLevelUpFee를 생성하게
  function setLevelUpFee(uint _fee) external onlyOwner {
    levelUpFee = _fee;
  }
  
..

3. 좀비 전투

이제는 본격적으로 코딩을 해볼 시간이다.


정답

pragma solidity ^0.4.19;

import "./zombiehelper.sol";

contract ZombieBattle is ZombieHelper {

}

4. 난수 (Random Numbers)

난수는 말 그대로 무작위의 숫자다.
그렇다면 일단 프로그래밍에서처럼 rand()를 하면 될까?

블록체인 프로그래밍을 이해하려면 작업증명을 알아야 한다.
여러 컴퓨터들이 작업증명이라고 하는 어려운 문제를 푼다.
그리고 문제를 다 풀면, 함수를 실행하고 그 결과를 블록에 쌓아 배포한다.

그런데 만약, 문제를 푼 다음에 배포를 안 하면 어떻게 될까?
함수를 실행해본 다음 맘에 안 들면 배포를 안 하는 인간도 있을 것이다.
물론, 가능성이 몹시 낮기는 하지만 가능은 하다는 얘기다.

그렇기 때문에 난수생성은 이 튜토리얼을 벗어나는 얘기라고 한다.
아마 영원한 난제일수도 있다.
나중에는 이더리움 외부에서 데이터를 받아오는 oracle을 사용해볼수도 있다.


정답

...

  // 여기서 시작하게
  uint randNonce = 0;

  function randMod(uint _modulus) internal returns(uint) {
    randNonce++;
    return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
  }


...

5. 좀비 싸움

말이 좀 긴데 좀비 싸움의 진행방식은 다음과 같다고 한다.

1. 내 좀비들 중 하나를 고르고 상대방의 좀비를 선택한다.
2. 공격하는쪽은 70%의 확률로 승리하고, 방어하는 쪽은 30%의 확률로 승리한다.
3. 전투 결과에 따라 증가하는 winCount와 lossCount가 생긴다.
4. 공격하는 쪽의 좀비가 이기면, 좀비의 레벨이 오른다.
5. 좀비가 이기든 지든, 공격하는 쪽 좀비의 재사용시간이 활성화될 것이다.

정답

...

  // 여기에 attackVictoryProbability를 만들게
  uint attackVictoryProbability = 70;

...

  // 여기에 새로운 함수를 만들게
  function attack(uint _zombieId, uint _targetId) external {
    
  }

...

6. 공통 로직 구조 개선하기 (Refactoring, 리팩토링)

여러 함수에서 공통적으로 쓰이는,
좀비가 특정 소유자의 것인가를 확인하는 로직을
사용자 정의 함수 제어자로 통일한다.


정답

...

  // 1. 여기에 제어자를 생성하게
  modifier ownerOf(uint _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    _;
  }
  
  ...
  
  // 2. 함수 정의 부분에 제어자를 추가하게:
  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
    // 3. 이 줄을 지우게
    
 ...

7. 구조 더 개선하기

정답

...

  // 1. 이 함수를 `ownerOf`를 사용하도록 변경하게:
  function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
    zombies[_zombieId].name = _newName;
  }

  // 2. 이 함수에도 똑같이 적용하게:
  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
    zombies[_zombieId].dna = _newDna;
  }
  
...

8. 공격으로 돌아가자!

정답

...

  // 1. 여기에 제어자를 추가하게
  function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
    // 2. 여기서 함수 정의를 시작하게
    Zombie storage myZombie = zombies[_zombieId];
    Zombie storage enemyZombie = zombies[_targetId];

    uint rand = randMod(100);
  }
  
...

9. 좀비 승리와 패배

Zombie 구조체에 win과 loss를 세는 변수를 넣는다.


정답

...

// 1. 여기에 새로운 속성을 추가하게
      uint16 winCount;
      uint16 lossCount;
    }

...

        // 2. 여기서 새로운 좀비의 생성을 수정하게:
        uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;

...

10. 좀비 승리

정답

...

    // 여기서 시작하게
    if (rand <= attackVictoryProbability) {
      myZombie.winCount++;
      myZombie.level++;
      enemyZombie.lossCount++;
      feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
    }
    
 ...

11. 좀비 패배

정답

...

    } // 여기서 시작하게
    else {
      myZombie.lossCount++;
      enemyZombie.winCount++;
    }

    _triggerCooldown(myZombie);
    
...

후기

이번 챕터에서는 새로운 것을 배우기보다는
실질적인 기능을 코딩하는 경우가 많았다.

그래서 코딩테스트를 보는것 같아 재미있었다.
확실히 자바 스크립트를 잘 쓰니까 솔리디티 하기 편한 것 같다.