{
  "language": "Solidity",
  "sources": {
    "LittleFrenGame.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\n/// @notice One non-transferable pet per wallet. No admin, custody, payable methods or external calls.\ncontract LittleFrenGame {\n    struct Pet { string name; uint64 coins; uint64 xp; uint64 lastAction; uint8 energy; uint8 fullness; uint8 mood; uint8 skin; uint8 tasks; bool claimed; bool exists; }\n    mapping(address => Pet) private pets;\n    struct Outing { uint8[3] counts; uint8 trail; uint64 readyAt; bool active; }\n    mapping(address => Outing) private outings;\n    event OutingStarted(address indexed player, uint8 trail, uint64 readyAt);\n    event KeepsakeCollected(address indexed player, uint8 trail, uint8 item);\n    error TrailLocked(); error TrailComplete(); error OutingActive(); error NoOuting(); error OutingNotReady();\n    function getOuting(address player) external view returns(Outing memory) { return outings[player]; }\n    function startOuting(uint8 trail) external {\n        Pet storage p=pet();\n        if(trail>2) revert InvalidAction();\n        if(p.xp<uint256(trail)*100) revert TrailLocked();\n        Outing storage o=outings[msg.sender];\n        if(o.active) revert OutingActive();\n        if(o.counts[trail]>=3) revert TrailComplete();\n        o.trail=trail;o.readyAt=uint64(block.timestamp+30*(uint256(trail)+1));o.active=true;\n        emit OutingStarted(msg.sender,trail,o.readyAt);\n    }\n    function collectOuting() external {\n        pet();Outing storage o=outings[msg.sender];\n        if(!o.active) revert NoOuting();\n        if(block.timestamp<o.readyAt) revert OutingNotReady();\n        uint8 item=o.counts[o.trail];o.counts[o.trail]=item+1;o.active=false;\n        emit KeepsakeCollected(msg.sender,o.trail,item);\n    }\n    uint256 public constant COOLDOWN = 8;\n    event Adopted(address indexed player, string name);\n    event Played(address indexed player, uint8 action, uint64 xp, uint64 coins);\n    event Renamed(address indexed player, string name);\n    event ColorChanged(address indexed player, uint8 skin);\n    event RewardClaimed(address indexed player);\n    error AlreadyAdopted(); error NoPet(); error InvalidName(); error InvalidAction();\n    error InsufficientPebbles(); error InsufficientEnergy(); error RestFirst(uint256 readyAt);\n    error ColorLocked(); error QuestIncomplete(); error AlreadyClaimed();\n    function getPet(address player) external view returns(Pet memory) { return pets[player]; }\n    function adopt(string calldata name) external {\n        if(pets[msg.sender].exists) revert AlreadyAdopted();\n        validateName(name);\n        pets[msg.sender] = Pet(name,40,0,0,70,55,85,0,0,false,true);\n        emit Adopted(msg.sender,name);\n    }\n    function play(uint8 action) external {\n        Pet storage p = pet();\n        if(action>3) revert InvalidAction();\n        if(block.timestamp < uint256(p.lastAction)+COOLDOWN) revert RestFirst(uint256(p.lastAction)+COOLDOWN);\n        if(action==0){\n            if(p.coins<10) revert InsufficientPebbles();\n            p.coins-=10; p.fullness=cap(uint256(p.fullness)+25); p.mood=cap(uint256(p.mood)+5); p.xp+=10; p.tasks|=1;\n        } else if(action==1){\n            if(p.energy<15) revert InsufficientEnergy();\n            p.energy-=15; p.fullness=sub(p.fullness,8); p.mood=cap(uint256(p.mood)+3);p.coins+=5;p.xp+=25;p.tasks|=2;\n        } else if(action==2){\n            if(p.energy<20) revert InsufficientEnergy();\n            p.energy-=20;p.fullness=sub(p.fullness,10);p.mood=cap(uint256(p.mood)+8);p.coins+=25;p.xp+=20;p.tasks|=4;\n        } else { p.energy=cap(uint256(p.energy)+35);p.mood=cap(uint256(p.mood)+4); }\n        p.lastAction=uint64(block.timestamp);\n        emit Played(msg.sender,action,p.xp,p.coins);\n    }\n    function rename(string calldata name) external {Pet storage p=pet();validateName(name);p.name=name;emit Renamed(msg.sender,name);}\n    function setColor(uint8 skin) external {Pet storage p=pet();if(skin>2||uint256(skin)*100>p.xp)revert ColorLocked();p.skin=skin;emit ColorChanged(msg.sender,skin);}\n    function claimQuest() external {Pet storage p=pet();if(p.claimed)revert AlreadyClaimed();if((p.tasks&7)!=7)revert QuestIncomplete();p.claimed=true;p.coins+=40;p.xp+=25;emit RewardClaimed(msg.sender);}\n    function pet() private view returns(Pet storage p){p=pets[msg.sender];if(!p.exists)revert NoPet();}\n    function cap(uint256 n) private pure returns(uint8){return uint8(n>100?100:n);}\n    function sub(uint8 n,uint8 amount) private pure returns(uint8){return n<amount?0:n-amount;}\n    function validateName(string calldata name) private pure {bytes memory b=bytes(name);if(b.length==0||b.length>20)revert InvalidName();bool visible;for(uint i;i<b.length;i++){if(uint8(b[i])<32||uint8(b[i])>126)revert InvalidName();if(b[i]!=0x20)visible=true;}if(!visible)revert InvalidName();}\n}\r\n"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "evmVersion": "shanghai",
    "outputSelection": {
      "*": {
        "*": [
          "abi",
          "evm.bytecode.object",
          "evm.deployedBytecode.object"
        ]
      }
    }
  }
}