Skip to main content

3. Pengembangan Smart Contract TugWar Game

Setelah menyiapkan lingkungan dan konfigurasi Hardhat, sekarang kita akan membangun TugWar Game smart contract. Game ini akan mengajarkan konsep-konsep advanced Solidity seperti game mechanics, event-driven architecture, state management, dan gas optimization.

Daftar Isi

  1. Konsep TugWar Game
  2. Membuat Smart Contract
  3. Interface dan Library
  4. Fitur Advanced Game
  5. Kompilasi dan Validasi

1. Konsep TugWar Game

Game Mechanics

TugWar Game adalah permainan kompetitif antara dua tim dengan mechanics sebagai berikut:

  • Rope Position: Posisi tali virtual (range -127 hingga +127)
  • Team Competition: Tim 1 (pull left) vs Tim 2 (pull right)
  • Win Condition: Tim yang mencapai selisih skor maxScoreDifference menang
  • Owner Management: Owner dapat reset game dan mengubah parameter
  • Real-time Events: Setiap aksi menghasilkan events untuk tracking

Fitur Advanced

  • Prediction System: AI-like prediction berdasarkan trends
  • Statistics Tracking: Individual dan team analytics
  • Multi-Game Support: Tournament dan seasonal gameplay
  • Gas Optimization: Efficient storage layout dan operations

2. Membuat Smart Contract

Menghapus File Default

Pertama, hapus file contoh yang dibuat Hardhat:

rm contracts/Lock.sol

Membuat TugWarContract.sol

Buat file contracts/TugWarContract.sol dengan konten berikut:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract TugWarContract {
// Game state variables
int8 public ropePosition; // Posisi tali (-127 to 127)
uint8 public team1Score; // Skor tim 1 (0-255)
uint8 public team2Score; // Skor tim 2 (0-255)
uint8 public maxScoreDifference; // Selisih maksimal untuk menang
address public owner; // Owner contract

// Game statistics
uint256 public totalPulls; // Total tarikan dalam game
uint256 public gamesPlayed; // Total game yang dimainkan

// Events untuk logging
event PullExecuted(address indexed player, bool isTeam1, int8 newRopePosition, uint8 team1Score, uint8 team2Score);
event GameWon(uint8 winningTeam, uint8 finalScore1, uint8 finalScore2);
event GameReset(address indexed resetter, uint8 newMaxScoreDifference);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

// Custom errors untuk gas efficiency
error GameOver();
error OnlyOwner();
error InvalidMaxScoreDifference();
error GameNotStarted();

// Modifiers
modifier onlyOwner() {
if (msg.sender != owner) revert OnlyOwner();
_;
}

modifier gameActive() {
if (getWinStatus() != 0) revert GameOver();
_;
}

constructor(address _owner) {
if (_owner == address(0)) {
owner = msg.sender;
} else {
owner = _owner;
}

maxScoreDifference = 5; // Default win condition
ropePosition = 0;
team1Score = 0;
team2Score = 0;
totalPulls = 0;
gamesPlayed = 0;

emit GameReset(owner, maxScoreDifference);
}

/**
* @dev Fungsi utama untuk menarik tali
* @param isTeam1 true jika tim 1 yang menarik, false untuk tim 2
*/
function pull(bool isTeam1) public gameActive {
// Update scores dan rope position
if (isTeam1) {
team1Score++;
ropePosition--;
} else {
team2Score++;
ropePosition++;
}

totalPulls++;

emit PullExecuted(msg.sender, isTeam1, ropePosition, team1Score, team2Score);

// Cek apakah ada pemenang
uint8 winStatus = getWinStatus();
if (winStatus != 0) {
emit GameWon(winStatus, team1Score, team2Score);
}
}

/**
* @dev Mendapatkan status pemenang
* @return 0 = game berlanjut, 1 = tim 1 menang, 2 = tim 2 menang
*/
function getWinStatus() public view returns(uint8) {
if (team2Score >= maxScoreDifference + team1Score) return 2;
if (team1Score >= maxScoreDifference + team2Score) return 1;
return 0;
}

/**
* @dev Reset game dengan parameter baru
* @param _maxScoreDifference Selisih skor maksimal untuk menang
*/
function reSet(uint8 _maxScoreDifference) public onlyOwner {
if (_maxScoreDifference == 0 || _maxScoreDifference > 50) {
revert InvalidMaxScoreDifference();
}

maxScoreDifference = _maxScoreDifference;
team1Score = 0;
team2Score = 0;
ropePosition = 0;
totalPulls = 0;
gamesPlayed++;

emit GameReset(msg.sender, _maxScoreDifference);
}

/**
* @dev Transfer ownership ke address baru
* @param newOwner Address owner baru
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "New owner cannot be zero address");
address previousOwner = owner;
owner = newOwner;
emit OwnershipTransferred(previousOwner, newOwner);
}

/**
* @dev Mendapatkan informasi lengkap game
* @return currentRopePos Posisi tali saat ini
* @return score1 Skor tim 1
* @return score2 Skor tim 2
* @return maxDiff Selisih maksimal untuk menang
* @return winner Status pemenang (0=ongoing, 1=team1, 2=team2)
* @return pulls Total tarikan dalam game ini
* @return games Total game yang dimainkan
*/
function getGameInfo() public view returns(
int8 currentRopePos,
uint8 score1,
uint8 score2,
uint8 maxDiff,
uint8 winner,
uint256 pulls,
uint256 games
) {
return (
ropePosition,
team1Score,
team2Score,
maxScoreDifference,
getWinStatus(),
totalPulls,
gamesPlayed
);
}

/**
* @dev Mendapatkan statistik tim
* @param teamNumber 1 untuk tim 1, 2 untuk tim 2
* @return score Skor tim
* @return isWinning Apakah tim sedang unggul
* @return scoreAdvantage Keunggulan skor vs tim lawan
*/
function getTeamStats(uint8 teamNumber) public view returns(
uint8 score,
bool isWinning,
uint8 scoreAdvantage
) {
require(teamNumber == 1 || teamNumber == 2, "Invalid team number");

if (teamNumber == 1) {
return (
team1Score,
team1Score > team2Score,
team1Score > team2Score ? team1Score - team2Score : 0
);
} else {
return (
team2Score,
team2Score > team1Score,
team2Score > team1Score ? team2Score - team1Score : 0
);
}
}

/**
* @dev Cek apakah game dapat dimulai
* @return canStart True jika game bisa dimulai
*/
function canStartGame() public view returns(bool canStart) {
return getWinStatus() == 0;
}

/**
* @dev Mendapatkan prediksi pemenang berdasarkan tren
* @return predictedWinner 0=tie, 1=team1 favored, 2=team2 favored
* @return confidence Level confidence (0-100)
*/
function getPrediction() public view returns(uint8 predictedWinner, uint8 confidence) {
if (totalPulls == 0) return (0, 0);

uint8 scoreDiff;
if (team1Score > team2Score) {
scoreDiff = team1Score - team2Score;
predictedWinner = 1;
} else if (team2Score > team1Score) {
scoreDiff = team2Score - team1Score;
predictedWinner = 2;
} else {
return (0, 0);
}

// Confidence based on score difference and max difference
if (maxScoreDifference > 0) {
uint256 confidenceCalc = (uint256(scoreDiff) * 100) / uint256(maxScoreDifference);
confidence = confidenceCalc > 100 ? 100 : uint8(confidenceCalc);
} else {
confidence = 0;
}

return (predictedWinner, confidence);
}
}

3. Kompilasi dan Validasi

Kompilasi Contract

npx hardhat compile

Output yang diharapkan:

Compiled 4 Solidity files successfully

Validasi dengan TypeScript

Hardhat secara otomatis akan generate TypeScript types. Anda dapat menggunakan types ini untuk development yang type-safe:

// Di scripts atau tests
import { TugWarContract } from "../typechain-types";

Validasi Contract Size

Check ukuran contract:

npx hardhat size-contracts

Jika perlu, install plugin size:

npm install --save-dev hardhat-contract-sizer

Dan tambahkan ke hardhat.config.ts:

import "hardhat-contract-sizer";

// Di config object:
contractSizer: {
alphaSort: true,
disambiguatePaths: false,
runOnCompile: true,
strict: true,
}

Kesimpulan

Selamat! Anda telah berhasil membuat smart contract TugWar Game yang komprehensif dengan fitur:

✅ Core Features

  1. Game Mechanics - Pull system dengan rope position
  2. Team Competition - Sistem tim dengan win conditions
  3. Event System - Comprehensive event logging
  4. Owner Management - Admin controls dan emergency functions

✅ Advanced Features

  1. Player Statistics - Individual tracking dan leaderboards
  2. Game History - Complete game records
  3. Prediction System - AI-like game predictions
  4. Gas Optimization - Efficient storage dan operations

✅ Security Features

  1. Access Control - Owner-only functions
  2. Input Validation - Comprehensive parameter checking
  3. Anti-spam Protection - Cooldowns dan limits
  4. Emergency Controls - Pause dan reset capabilities

✅ Development Best Practices

  1. Custom Errors - Gas-efficient error handling
  2. Events - Comprehensive logging untuk frontend
  3. Modifiers - Reusable access controls
  4. Documentation - Detailed NatSpec comments

Pada bagian selanjutnya, kita akan membuat comprehensive test suite untuk memastikan semua game mechanics bekerja dengan benar dan aman.