Blockchain / Global / Address

Contract

0x35a7f3eE9A2b5fdEE717099F9253Ae90e1248AE3

Balance
0.062 SOMI
Transactions sent
1
Type
Contract · 3,449 bytes of code

PacmanLeaderboard Verified

Compiler
solc 0.8.20+commit.a1b79de6
Language
Solidity
Optimization
Yes, 200 runs
EVM version
—
License
—
Verified
2026-10-12

PacmanLeaderboard.sol

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract PacmanLeaderboard {
    address public owner;
    address public treasury; // Alamat untuk menerima biaya game
    uint256 public startFeeWei;
    uint256 public maxScorePerSubmit;

    struct Player {
        uint256 totalScore;
        uint256 lastPlayed;
    }

    mapping(address => Player) public players;

    // STORAGE BARU: Array tetap 10 slot untuk leaderboard yang efisien gas
    address[10] public top10Wallets;
    uint256[10] public top10Scores;

    event GameStarted(address indexed player, uint256 fee);
    event ScoreSubmitted(address indexed player, uint256 score);
    event FeeUpdated(uint256 newFee);
    event MaxScoreUpdated(uint256 newMaxScore);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event Withdraw(address indexed to, uint256 amount);

    constructor(
        address _treasury,
        uint256 _startFeeWei,
        uint256 _maxScorePerSubmit
    ) {
        // Owner default adalah yang melakukan deploy (msg.sender)
        owner = msg.sender; 
        treasury = _treasury;
        startFeeWei = _startFeeWei;
        maxScorePerSubmit = _maxScorePerSubmit;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    // Fungsi internal untuk memasukkan skor baru ke dalam Top 10 secara efisien
    function _updateTop10(address player, uint256 newTotalScore) internal {
        int256 insertIndex = -1;
        
        // 1. Cari posisi yang cocok (skor lebih besar dari skor di slot tersebut)
        for (uint256 i = 0; i < 10; i++) {
            if (newTotalScore > top10Scores[i]) {
                insertIndex = int256(i);
                break;
            } else if (top10Wallets[i] == player) {
                // Jika pemain sudah ada di Top 10, update skor dan sortir ulang
                top10Scores[i] = newTotalScore;
                _sortTop10(); 
                return;
            }
        }
        
        // 2. Jika skor baru cukup tinggi untuk masuk Top 10
        if (insertIndex != -1) {
            // Geser entri yang lebih rendah ke bawah
            for (int256 i = 9; i > insertIndex; i--) {
                top10Scores[uint256(i)] = top10Scores[uint256(i - 1)];
                top10Wallets[uint256(i)] = top10Wallets[uint256(i - 1)];
            }
            // Masukkan skor baru
            top10Scores[uint256(insertIndex)] = newTotalScore;
            top10Wallets[uint256(insertIndex)] = player;
        }
    }

    // Helper: Sortir ulang array 10 item
    function _sortTop10() internal {
        for (uint256 i = 0; i < 10; i++) {
            for (uint256 j = i + 1; j < 10; j++) {
                if (top10Scores[j] > top10Scores[i]) {
                    (top10Scores[i], top10Scores[j]) = (top10Scores[j], top10Scores[i]);
                    (top10Wallets[i], top10Wallets[j]) = (top10Wallets[j], top10Wallets[i]);
                }
            }
        }
    }

    function startGame() external payable {
        require(msg.value == startFeeWei, "Wrong game fee");
        
        players[msg.sender].lastPlayed = block.timestamp;

        emit GameStarted(msg.sender, msg.value);
    }

    function submitScore(uint256 score) external {
        require(score <= maxScorePerSubmit, "Score too high");
        require(players[msg.sender].lastPlayed != 0, "Start game first");

        // Akumulasi total skor pemain
        players[msg.sender].totalScore += score;
        players[msg.sender].lastPlayed = 0; // Reset status permainan

        // Panggil fungsi scalable untuk memperbarui leaderboard
        _updateTop10(msg.sender, players[msg.sender].totalScore);

        emit ScoreSubmitted(msg.sender, score);
    }

    // --- FUNGSI ADMIN UNTUK FLEKSIBILITAS ---

    function setStartFee(uint256 newFee) external onlyOwner {
        startFeeWei = newFee;
        emit FeeUpdated(newFee);
    }

    function setMaxScore(uint256 newMaxScore) external onlyOwner {
        maxScorePerSubmit = newMaxScore;
        emit MaxScoreUpdated(newMaxScore);
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "New owner is zero address");
        address previousOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(previousOwner, newOwner);
    }

    // --- FUNGSI RETRIEVAL (EFEK GAS) ---

    function getTop10()
        external
        view
        returns (address[] memory topPlayers, uint256[] memory scores)
    {
        uint256 count = 0;
        for (uint256 i = 0; i < 10; i++) {
            if (top10Wallets[i] != address(0) && top10Scores[i] > 0) {
                count++;
            }
        }

        topPlayers = new address[](count);
        scores = new uint256[](count);
        
        for (uint256 i = 0; i < count; i++) {
            topPlayers[i] = top10Wallets[i];
            scores[i] = top10Scores[i];
        }
    }

    function withdraw() external onlyOwner {
        uint256 bal = address(this).balance;
        require(bal > 0, "Empty");

        payable(treasury).transfer(bal); // Menggunakan variabel treasury
        emit Withdraw(treasury, bal);
    }
}
ABI · 21 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_treasury",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "_startFeeWei",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "_maxScorePerSubmit",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "FeeUpdated",
    "type": "event",
    "inputs": [
      {
        "name": "newFee",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "GameStarted",
    "type": "event",
    "inputs": [
      {
        "name": "player",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "fee",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "MaxScoreUpdated",
    "type": "event",
    "inputs": [
      {
        "name": "newMaxScore",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipTransferred",
    "type": "event",
    "inputs": [
      {
        "name": "previousOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ScoreSubmitted",
    "type": "event",
    "inputs": [
      {
        "name": "player",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "score",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Withdraw",
    "type": "event",
    "inputs": [
      {
        "name": "to",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "getTop10",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "topPlayers",
        "type": "address[]",
        "internalType": "address[]"
      },
      {
        "name": "scores",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "maxScorePerSubmit",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "owner",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "players",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "totalScore",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "lastPlayed",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setMaxScore",
    "type": "function",
    "inputs": [
      {
        "name": "newMaxScore",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setStartFee",
    "type": "function",
    "inputs": [
      {
        "name": "newFee",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "startFeeWei",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "startGame",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "submitScore",
    "type": "function",
    "inputs": [
      {
        "name": "score",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "top10Scores",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "top10Wallets",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "transferOwnership",
    "type": "function",
    "inputs": [
      {
        "name": "newOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "treasury",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "withdraw",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  }
]