[카톡봇] 낚시를 해서 로또를 사자

GoGoComputer·2024년 11월 21일

makeBot

목록 보기
1/1
post-thumbnail

안녕하세요! 오늘은 함께 재미있는 낚시 게임 코드를 살펴보려고 해요. 이 코드는 채팅방에서 여러 명령어를 사용하여 낚시를 하고, 물고기를 거래하고, 로또를 구매하는 등 다양한 기능을 제공해요. 중학교 수준에서 이해하기 쉽게 하나씩 설명해볼게요.

메신저봇r 에서 작동 합니다.


1. 전체적인 구조

이 코드는 채팅방에서 명령어를 입력하면 그에 맞는 기능을 수행하는 프로그램이에요. 예를 들어 "/낚시 시작"이라고 입력하면 낚시를 시작하고, 일정 시간이 지나면 랜덤한 물고기를 낚을 수 있어요.


2. 사용자 데이터 관리

  • userData: 각 사용자의 정보를 저장하는 객체에요. 여기에는 인벤토리(낚은 물고기 목록), 레벨, 경험치, 골드 등이 저장되어요.
  • isFishing: 사용자가 현재 낚시 중인지 아닌지를 저장하는 객체에요.
const userData = {};
const isFishing = {};

3. 물고기 목록

  • fishs: 낚을 수 있는 물고기들의 이름이 배열로 저장되어 있어요. 여기에는 실제 물고기뿐만 아니라 재미있는 아이템들도 포함되어 있어요.
const fishs = [
  "해파리", "말미잘", "새우", "가재", "고래", "문어", "오징어",
  // ... 생략 ...
  "대왕오징어", "귀신고래", "미크로네시아달팽이",
];

4. 명령어 처리

  • response 함수: 사용자가 채팅방에 입력한 메시지를 처리하는 함수에요. 이 함수는 여러 명령어를 인식하고, 그에 맞는 기능을 수행해요.
function response(room, msg, sender, igc, replier) {
  // 명령어를 처리하는 여러 조건문들...
}

5. 낚시 관련 기능

1) 낚시 시작

  • 명령어: /낚시 시작
  • 설명: 사용자가 낚시를 시작해요.
  • 동작 과정:
    • 사용자가 이미 낚시 중인지 확인해요.
    • 낚시 중이 아니면 낚시를 시작하고, 잠시 후에 물고기를 낚아요.
if (msg === "/낚시 시작") {
  checkUser(sender);
  if (!isFishing[sender]) {
    startFishing(sender, userData[sender], replier);
  } else {
    replier.reply("이미 낚시 중입니다");
  }
}

2) 낚시 레벨 확인

  • 명령어: /낚시 레벨
  • 설명: 사용자의 낚시 레벨과 다음 레벨까지 필요한 경험치를 보여줘요.
else if (msg === "/낚시 레벨") {
  checkUser(sender);
  replier.reply(
    sender +
      "님의 낚시 레벨은 " +
      userData[sender].level +
      "입니다\n" +
      "다음 레벨까지 필요한 경험치: " +
      userData[sender].point +
      "/" +
      getPoint(userData[sender].level)
  );
}

3) 인벤토리 확인

  • 명령어: /인벤토리
  • 설명: 사용자가 지금까지 낚은 물고기 목록을 보여줘요.
else if (msg === "/인벤토리") {
  checkUser(sender);
  replier.reply(getInventory(sender));
}

4) 낚시 랭킹 확인

  • 명령어: /낚시 랭킹
  • 설명: 모든 사용자들의 낚시 랭킹을 보여줘요. 가장 큰 물고기를 낚은 순서대로 정렬돼요.
else if (msg === "/낚시 랭킹") {
  try {
    const rankData = getRank();
    const replyMessage = [
      "🎣 낚시 랭킹입니다 🎣",
      "-----------------------",
      rankData,
      "-----------------------",
      "랭킹에 오르고 싶다면 더 열심히 낚시하세요!",
    ].join("\n");
    replier.reply(replyMessage);
  } catch (error) {
    replier.reply(`에러 발생: ${error.message}`);
  }
}

6. 물고기 거래 기능

1) 물고기 판매

  • 명령어: /판매 [물고기이름] [크기] [가격]
  • 설명: 인벤토리에 있는 물고기를 다른 사용자에게 판매할 수 있어요.
else if (msg.startsWith("/판매 ")) {
  let parts = msg.split(" ");
  if (parts.length === 4) {
    let fishName = parts[1];
    let fishSize = parseInt(parts[2]);
    let price = parseInt(parts[3]);
    if (!isNaN(fishSize) && !isNaN(price)) {
      sellFish(sender, fishName, fishSize, price, replier);
    } else {
      replier.reply(
        "올바른 형식으로 입력해주세요. 예: /판매 [물고기이름] [크기] [가격]"
      );
    }
  }
}

2) 물고기 구매

  • 명령어: /구매 [판매자] [물고기이름] [크기]
  • 설명: 다른 사용자가 판매 중인 물고기를 구매할 수 있어요.
else if (msg.startsWith("/구매 ")) {
  let parts = msg.split(" ");
  if (parts.length === 4) {
    let seller = parts[1];
    let fishName = parts[2];
    let fishSize = parseInt(parts[3]);
    if (!isNaN(fishSize)) {
      buyFish(sender, seller, fishName, fishSize, replier);
    } else {
      replier.reply(
        "올바른 형식으로 입력해주세요. 예: /구매 [판매자] [물고기이름] [크기]"
      );
    }
  }
}

3) 수산시장 확인

  • 명령어: /수산시장
  • 설명: 현재 거래 중인 물고기 목록을 보여줘요.
else if (msg === "/수산시장") {
  checkMarket(replier);
}

7. 골드(게임 내 화폐) 관리

1) 골드 확인

  • 명령어: /골드
  • 설명: 사용자의 현재 골드량을 보여줘요.
else if (msg === "/골드") {
  checkGold(sender, replier);
}

2) 골드 발행 (관리자용)

  • 명령어: /골드발행9332!@# [사용자명] [골드량]
  • 설명: 특정 사용자에게 골드를 발행해요. (일반 사용자는 사용할 수 없어요)
else if (msg.startsWith("/골드발행9332!@# ")) {
  let parts = msg.split(" ");
  if (parts.length === 3) {
    let targetUser = parts[1];
    let amount = parseInt(parts[2]);
    if (!isNaN(amount)) {
      issueGold(targetUser, amount, replier);
    } else {
      replier.reply("발행할 골드량은 숫자여야 합니다.");
    }
  } else {
    replier.reply(
      "올바른 형식으로 입력해주세요. 예: /골드발행 [사용자명] [골드량]"
    );
  }
}

3) 상인에게 물고기 판매

  • 명령어: /상인판매
  • 설명: 인벤토리에 있는 모든 물고기를 상인에게 판매하고 골드를 받아요.
else if (msg === "/상인판매") {
  const totalGold = sellAllFishToMerchant(sender);
  replier.reply(
    sender +
      " 님, 인벤토리에 있는 모든 물고기를 판매하여 " +
      totalGold +
      " 골드를 획득하셨습니다."
  );
}

8. 로또 기능

1) 로또 구매

  • 명령어: /로또구매 [금액]
  • 설명: 로또를 구매하여 당첨금을 노릴 수 있어요.
if (msg.startsWith("/로또구매 ")) {
  processLottoPurchase(msg, sender, replier);
}

2) 로또 확인

  • 명령어: /로또확인
  • 설명: 자신이 구매한 로또 금액과 현재 당첨금, 마지막 당첨자를 확인할 수 있어요.
else if (msg === "/로또확인") {
  checkLotto(sender, replier);
}

3) 로또 추첨

  • 명령어: /로또추첨
  • 설명: 하루에 한 번 로또를 추첨하여 당첨자를 뽑아요.
else if (msg === "/로또추첨") {
  drawLotto(replier);
}

4) 로또 랭킹

  • 명령어: /999로또랭킹
  • 설명: 로또를 많이 구매한 순서대로 순위를 보여줘요.
else if (msg === "/999로또랭킹") {
  lottoRanking(replier);
}

9. 주요 함수 설명

1) checkUser(name)

  • 역할: 사용자가 처음 게임에 참여하는지 확인하고, 없다면 초기 데이터를 생성해요.
function checkUser(name) {
  if (!userData[name]) {
    userData[name] = {
      inventory: [],
      level: 1,
      point: 0,
      gold: 10000,
    };
    saveData();
  }
  if (!Object.prototype.hasOwnProperty.call(isFishing, name)) {
    isFishing[name] = false;
  }
}

2) saveData()

  • 역할: 현재까지의 사용자 데이터를 파일에 저장해요.
function saveData() {
  FileStream.write(PATH, JSON.stringify(userData));
}

3) startFishing(name, data, replier)

  • 역할: 낚시를 시작하고, 일정 시간이 지나면 물고기를 낚아요.
function startFishing(name, data, replier) {
  isFishing[name] = true;
  replier.reply(name + "님이 낚시를 시작했습니다");

  setTimeout(function () {
    replier.reply(getRandomFish(name, data, data.level));
    isFishing[name] = false;
  }, (10 + ((Math.random() * 20) | 0)) * 1000);
}

4) getRandomFish(name, data, level)

  • 역할: 랜덤한 물고기를 생성하고, 인벤토리에 추가해요.
function getRandomFish(name, data, level) {
  // 랜덤한 크기와 물고기 이름 생성
  // 인벤토리에 추가하고 경험치와 레벨 업데이트
  // 결과 메시지 반환
}

5) getInventory(name)

  • 역할: 사용자의 인벤토리를 문자열로 만들어 반환해요.
function getInventory(name) {
  let inventory = userData[name].inventory;
  if (inventory.length === 0) return "인벤토리가 비어 있습니다.";
  return (
    "\u200b".repeat(1000) +
    inventory
      .sort((y, x) => x[0] - y[0])
      .map((x) => x[0] + "cm " + x[1])
      .join("\n")
  );
}

6) sellFish(seller, fishName, fishSize, price, replier)

  • 역할: 사용자의 인벤토리에서 물고기를 찾아 판매 목록에 등록해요.
function sellFish(seller, fishName, fishSize, price, replier) {
  // 인벤토리에서 해당 물고기 검색
  // 판매 목록에 추가
  // 인벤토리에서 제거
  // 저장 및 메시지 출력
}

7) buyFish(buyer, seller, fishName, fishSize, replier)

  • 역할: 구매자가 판매자의 물고기를 구매하고, 골드를 거래해요.
function buyFish(buyer, seller, fishName, fishSize, replier) {
  // 판매 목록에서 해당 물고기 검색
  // 구매자의 골드 확인
  // 골드 거래 및 물고기 이동
  // 저장 및 메시지 출력
}

8) checkMarket(replier)

  • 역할: 현재 판매 중인 물고기 목록을 보여줘요.
function checkMarket(replier) {
  // 판매 목록을 순회하며 문자열 생성
  // 메시지 출력
}

9) checkGold(sender, replier)

  • 역할: 사용자의 현재 골드량을 보여줘요.
function checkGold(sender, replier) {
  checkUser(sender);
  const currentGold = userData[sender].gold;
  replier.reply(sender + " 님의 현재 골드는 " + currentGold + " 골드입니다.");
}

10) issueGold(targetUser, amount, replier)

  • 역할: 특정 사용자에게 골드를 발행해요. (관리자용)
function issueGold(targetUser, amount, replier) {
  checkUser(targetUser);
  userData[targetUser].gold += amount;
  saveData();
  replier.reply(
    targetUser +
      " 님에게 " +
      amount +
      " 골드가 발행되었습니다. 현재 골드: " +
      userData[targetUser].gold
  );
}

11) sellAllFishToMerchant(name)

  • 역할: 사용자의 인벤토리에 있는 모든 물고기를 상인에게 판매해요.
function sellAllFishToMerchant(name) {
  checkUser(name);
  const inventory = userData[name].inventory;
  const fishCount = inventory.length;
  const pricePerFish = 5000;
  const totalGold = fishCount * pricePerFish;

  // 인벤토리 비우기
  userData[name].inventory = [];
  // 골드 추가
  userData[name].gold += totalGold;
  // 데이터 저장
  saveData();

  return totalGold;
}

12) 로또 관련 함수들

  • buyLotto(sender, amount, replier): 로또를 구매해요.
  • checkLotto(sender, replier): 자신의 로또 구매 정보와 현재 당첨금 등을 확인해요.
  • drawLotto(replier): 로또를 추첨하고 당첨자를 결정해요.
  • lottoRanking(replier): 로또 구매 금액 순위를 보여줘요.

10. 데이터 저장 및 로드

  • 데이터 저장: 사용자 데이터와 로또 데이터는 파일에 저장되어, 프로그램이 종료되더라도 정보가 유지돼요.
function saveData() {
  FileStream.write(PATH, JSON.stringify(userData));
}
  • 데이터 로드: 프로그램이 시작될 때 저장된 데이터를 불러와요.
function loadData() {
  let fileContent = FileStream.read(PATH);
  if (!fileContent || fileContent.trim() === "") {
    userData = {};
    saveData();
  } else {
    userData = JSON.parse(fileContent);
  }
}

11. 마무리

이 코드는 사용자들이 채팅방에서 재미있게 낚시를 하고, 물고기를 거래하고, 로또를 구매하며 놀 수 있도록 만들어졌어요. 각 기능마다 사용자의 입력을 처리하고, 그에 따른 결과를 보여주는 방식으로 구성되어 있어요.

중학교 수준에서 최대한 쉽게 설명하려고 노력했는데, 이해하는 데 도움이 되었으면 좋겠어요! 궁금한 점이 있으면 언제든지 물어봐주세요. 😊

전체 코드

/*

20241119 make 2.01 최신 버전

물고기 추가


Runtime Emor(낚시 게임)
ReferenceError: "fs" is not defined. (#4|
검
확인
게임#232) at 낚시 게임:232 (saveData)
at 낚시 게임:407 8)*/

/* const sdcard =
  android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
const PATH = "/darkapple/fishing/userData.txt"; // 파일 경로 설정
// 파일 입출력을 위한 객체 생성
function saveData() {
  FileStream.write(PATH, JSON.stringify(userData));
}


*/

if (typeof FileStream === "undefined") {
  throw new Error("FileStream 객체를 사용할 수 없습니다.");
}

function saveData() {
  FileStream.write(PATH, JSON.stringify(userData));
}

const PATH =
  android.os.Environment.getExternalStorageDirectory().getAbsolutePath() +
  "/darkapple/fishing/userDatagame.txt";
/*
  let userData = {};
  
  // 파일 읽기
  let fileContent = FileStream.read(PATH);
  if (!fileContent || fileContent.trim() === "") {
    // 파일이 없거나 내용이 비어있을 경우 초기화
    userData = {};
    FileStream.write(PATH, JSON.stringify(userData));
  } else {
    // 파일 내용이 있을 경우 JSON 파싱
    userData = JSON.parse(fileContent);
  } */

////////////////////////////////////////////////////////////////

// 데이터 저장 함수
function saveData() {
  FileStream.write(PATH, JSON.stringify(userData));
}

// 데이터 로드 함수
function loadData() {
  let fileContent = FileStream.read(PATH);
  if (!fileContent || fileContent.trim() === "") {
    // 파일이 없거나 내용이 비어있을 경우 초기화
    userData = {};
    saveData();
  } else {
    // 파일 내용이 있을 경우 JSON 파싱
    userData = JSON.parse(fileContent);
  }
}

// 초기 데이터 로드
loadData();

///////////////////////////////////////////////////////////////

const isFishing = {};
const fishs = [
  "해파리",
  "말미잘",
  "새우",
  "가재",
  "고래",
  "문어",
  "오징어",
  "돌고래",
  "바다표범",
  "가다랑어",
  "가오리",
  "갈치",
  "개복치",
  "광어",
  "농어",
  "대구",
  "도미",
  "멸치",
  "민어",
  "방어",
  "복어",
  "상어",
  "아귀",
  "연어",
  "우럭",
  "장화",
  "임연수어",
  "잉어",
  "장어",
  "돈까스",
  "돌",
  "먼지",
  "감자",
  "고구마",
  "당근",
  "양파",
  "파",
  "마늘",
  "고양이",
  "강아지",
  "토끼",
  "거북이",
  "앵무새",
  "햄스터",
  "고슴도치",
  "펭귄",
  "돼지",
  "소",
  "양",
  "염소",
  "말",
  "닭",
  "오리",
  "참새",
  "독수리",
  "부엉이",
  "까마귀",
  "거위",
  "악어",
  "코끼리",
  "기린",
  "코뿔소",
  "하마",
  "코알라",
  "뚱이",
  "스펀지밥",
  "치킨",
  "햄버거",
  "샌드위치",
  "라면",
  "떡볶이",
  "순대",
  "튀김",
  "김밥",
  "초밥",
  "우동",
  "라멘",
  "파스타",
  "참치",
  "산호",
  "성게",
  "불가사리",
  "따개비",
  "해삼",
  "청새치",
  "전갱이",
  "흰동가리",
  "도화돔",
  "돌돔",
  "자리돔",
  "넙치",
  "노랑가오리",
  "복섬",
  "황복",
  "흑돔",
  "쑤기미",
  "까치복",
  "병어",
  "홍어",
  "청어",
  "독가시치",
  "참바리",
  "줄가자미",
  "보라성게",
  "거북손",
  "망둑어",
  "꼼치",
  "돌기해삼",
  "줄무늬전갱이",
  "블루탱",
  "플랑크톤",
  "줄도화돔",
  "갈색돔",
  "은어",
  "황새치",
  "산호게",
  "왕게",
  "대게",
  "청게",
  "피라루크",
  "해룡",
  "바다거북",
  "바닷말",
  "해우",
  "오리발고둥",
  "밤수지맨드라미",
  "바다달팽이",
  "바다뱀",
  "바다뱀장어",
  "큰가시고기",
  "아라바마주걱",
  "양볼락",
  "배스락피쉬",
  "황제엔젤피쉬",
  "레이나스해삼",
  "가리비",
  "대왕오징어",
  "귀신고래",
  "미크로네시아달팽이",
];

const market = {}; // 사용자 간 거래를 위한 판매 목록
function response(room, msg, sender, igc, replier) {
  if (msg.startsWith("/로또구매 ")) {
    processLottoPurchase(msg, sender, replier);
  } else if (msg === "/로또확인") {
    checkLotto(sender, replier);
  } else if (msg === "/로또추첨") {
    drawLotto(replier);
  } else if (msg === "/999로또랭킹") {
    lottoRanking(replier);
  } else if (msg === "/낚시 시작") {
    checkUser(sender);
    if (!isFishing[sender]) {
      startFishing(sender, userData[sender], replier);
    } else {
      replier.reply("이미 낚시 중입니다");
    }
  } else if (msg === "/낚시 레벨") {
    checkUser(sender);
    replier.reply(
      sender +
        "님의 낚시 레벨은 " +
        userData[sender].level +
        "입니다\n" +
        "다음 레벨까지 필요한 경험치: " +
        userData[sender].point +
        "/" +
        getPoint(userData[sender].level)
    );
  } else if (msg === "/인벤토리") {
    checkUser(sender);
    replier.reply(getInventory(sender));

    // "/낚시 랭킹" 명령어 처리
  } else if (msg === "/낚시 랭킹") {
    try {
      const rankData = getRank(); // 랭킹 데이터를 가져옴
      const replyMessage = [
        "🎣 낚시 랭킹입니다 🎣",
        "-----------------------",
        rankData,
        "-----------------------",
        "랭킹에 오르고 싶다면 더 열심히 낚시하세요!",
      ].join("\n");
      replier.reply(replyMessage);
    } catch (error) {
      replier.reply(`에러 발생: ${error.message}`);
    }
  } else if (msg.startsWith("/판매 ")) {
    let parts = msg.split(" ");
    if (parts.length === 4) {
      let fishName = parts[1];
      let fishSize = parseInt(parts[2]);
      let price = parseInt(parts[3]);
      if (!isNaN(fishSize) && !isNaN(price)) {
        sellFish(sender, fishName, fishSize, price, replier);
      } else {
        replier.reply(
          "올바른 형식으로 입력해주세요. 예: /판매 [물고기이름] [크기] [가격]"
        );
      }
    }
  } else if (msg.startsWith("/구매 ")) {
    let parts = msg.split(" ");
    if (parts.length === 4) {
      let seller = parts[1];
      let fishName = parts[2];
      let fishSize = parseInt(parts[3]);
      if (!isNaN(fishSize)) {
        buyFish(sender, seller, fishName, fishSize, replier);
      } else {
        replier.reply(
          "올바른 형식으로 입력해주세요. 예: /구매 [판매자] [물고기이름] [크기]"
        );
      }
    }
  } else if (msg === "/수산시장") {
    checkMarket(replier);
  } else if (msg === "/골드") {
    checkGold(sender, replier);
  } else if (msg.startsWith("/골드발행9332!@# ")) {
    let parts = msg.split(" ");
    if (parts.length === 3) {
      let targetUser = parts[1];
      let amount = parseInt(parts[2]);
      if (!isNaN(amount)) {
        issueGold(targetUser, amount, replier);
      } else {
        replier.reply("발행할 골드량은 숫자여야 합니다.");
      }
    } else {
      replier.reply(
        "올바른 형식으로 입력해주세요. 예: /골드발행 [사용자명] [골드량]"
      );
    }
  } else if (msg === "/상인판매") {
    const totalGold = sellAllFishToMerchant(sender);
    replier.reply(
      sender +
        " 님, 인벤토리에 있는 모든 물고기를 판매하여" +
        totalGold +
        " 골드를 획득하셨습니다."
    );
  }
  // 기존의 다른 명령어 처리 로직...
}

/* if (msg.startsWith("/골드추가9332!@# ")) {
    let parts = msg.split(" ");
    if (parts.length === 3) {
      let targetUser = parts[1];
      let amount = parseInt(parts[2]);
      if (!isNaN(amount)) {
        addGold(targetUser, amount, replier);
      } else {
        replier.reply("추가할 골드량은 숫자여야 합니다.");
      }
    } else {
      replier.reply(
        "올바른 형식으로 입력해주세요. 예: /골드추가 [사용자명] [골드량]"
      );
    }
  } */
//////////////////////////
// 낚시 게임 함수
//////////////////////////

function sellAllFishToMerchant(name) {
  checkUser(name);
  const inventory = userData[name].inventory;
  const fishCount = inventory.length;
  const pricePerFish = 5000;
  const totalGold = fishCount * pricePerFish;

  // 인벤토리 비우기
  userData[name].inventory = [];
  // 골드 추가
  userData[name].gold += totalGold;
  // 데이터 저장
  saveData();

  return totalGold;
}

function checkUser(name) {
  // 사용자가 없으면 새로 추가하며, 골드도 초기화함
  if (!userData[name]) {
    userData[name] = {
      inventory: [],
      level: 1,
      point: 0,
      gold: 10000, // 모든 사용자에게 초기 10000 골드 부여
    };
    saveData(); // 데이터를 저장하여 파일에 기록
  }
  if (!Object.prototype.hasOwnProperty.call(isFishing, name)) {
    isFishing[name] = false;
  }
}

function saveData() {
  FileStream.write(PATH, JSON.stringify(userData)); // userData를 파일에 저장
}

function startFishing(name, data, replier) {
  isFishing[name] = true;
  replier.reply(name + "\u202d님이 낚시를 시작했습니다");

  setTimeout(function () {
    replier.reply(getRandomFish(name, data, data.level));
    isFishing[name] = false;
  }, (10 + ((Math.random() * 20) | 0)) * 1000);
}

function checkLevel(data) {
  while (data.level < 100) {
    if (data.point > getPoint(data.level)) {
      data.level++;
    } else {
      break;
    }
  }
  saveData();
}

function getPoint(level) {
  return level * level * level + level * 500;
}

function getRandomFish(name, data, level) {
  var chance = Math.pow(level, 1 / 3) * 21;
  var result = (0.7 + Math.random() * 0.5) * level * 10;
  while (chance < Math.random() * 100) {
    result *= 1 + Math.random() * 0.04;
  }
  result = Math.floor(result);
  var fish = randomName();
  data.inventory.push([result, fish]);
  data.point += result;
  checkLevel(data);
  saveData();
  return name + "\u202d님이 " + result + "cm " + fish + " 를 낚았다.";
}

function randomName() {
  return fishs[(fishs.length * Math.random()) | 0];
}

function getInventory(name) {
  let inventory = userData[name].inventory;
  if (inventory.length === 0) return "인벤토리가 비어 있습니다.";
  return (
    "\u200b".repeat(1000) +
    inventory
      .sort((y, x) => x[0] - y[0])
      .map((x) => x[0] + "cm " + x[1])
      .join("\n")
  );
}

function getRank() {
  // userData가 정의되어 있고 객체인지 확인
  if (typeof userData !== "object" || userData === null) {
    throw new Error("userData가 정의되지 않았거나 올바른 객체가 아닙니다.");
  }

  // userData의 키들을 배열로 변환하여 정렬
  const sortedKeys = Object.keys(userData).sort((a, b) => {
    const inventoryA = Array.isArray(userData[a].inventory)
      ? userData[a].inventory
      : [];
    const inventoryB = Array.isArray(userData[b].inventory)
      ? userData[b].inventory
      : [];

    // 두 사용자의 인벤토리가 모두 비어있는 경우
    if (inventoryA.length === 0 && inventoryB.length === 0) return 0;
    if (inventoryA.length === 0) return 1;
    if (inventoryB.length === 0) return -1;

    // 각 사용자의 가장 큰 물고기의 크기를 비교하여 정렬
    const maxFishA = Math.max.apply(
      null,
      inventoryA.map((fish) => (Array.isArray(fish) ? fish[0] : -Infinity))
    );
    const maxFishB = Math.max.apply(
      null,
      inventoryB.map((fish) => (Array.isArray(fish) ? fish[0] : -Infinity))
    );

    return maxFishB - maxFishA;
  });

  // 정렬된 사용자 목록을 순회하며 순위 문자열 생성
  let result = "";
  sortedKeys.forEach((key, index) => {
    const bigFishInfo = getBigfish(key);
    result += index + 1 + "위: " + key + bigFishInfo + "\n\n";
  });

  return result.trim(); // 결과 문자열 반환
}

// 특정 사용자의 가장 큰 물고기 정보 가져오기
function getBigfish(userName) {
  const inventory = Array.isArray(userData[userName].inventory)
    ? userData[userName].inventory
    : [];
  if (inventory.length === 0) return "";
  const maxFish = inventory.reduce(
    (max, fish) => (Array.isArray(fish) && fish[0] > max[0] ? fish : max),
    [-Infinity, ""]
  );
  return maxFish;
}

function sellFish(seller, fishName, fishSize, price, replier) {
  checkUser(seller);

  let inventory = userData[seller].inventory;

  // 인벤토리에서 해당 물고기와 크기를 찾음
  let fishIndex = inventory.findIndex(
    (f) => f[1] === fishName && f[0] === fishSize
  );

  if (fishIndex === -1) {
    replier.reply("해당 크기의 물고기가 인벤토리에 없습니다.");
    return;
  }

  if (!market[seller]) {
    market[seller] = {};
  }

  // 판매 리스트에 추가
  market[seller][fishName + "-" + fishSize] = price;
  inventory.splice(fishIndex, 1); // 판매 등록된 물고기는 인벤토리에서 제거
  saveData();

  replier.reply(
    fishName + " " + fishSize + "cm를 " + price + " 골드에 판매했습니다."
  );
}

function buyFish(buyer, seller, fishName, fishSize, replier) {
  checkUser(buyer);
  checkUser(seller);

  if (!market[seller] || !market[seller][fishName + "-" + fishSize]) {
    replier.reply(
      seller +
        " 님이 " +
        fishSize +
        "cm 크기의 " +
        fishName +
        " 를 판매하지 않습니다."
    );
    return;
  }

  let price = market[seller][fishName + "-" + fishSize];
  if (userData[buyer].gold < price) {
    replier.reply(buyer + " 님의 골드가 부족합니다.");
    return;
  }

  // 거래 성립
  userData[buyer].gold -= price;
  userData[seller].gold += price;

  // 구매자의 인벤토리에 물고기 추가
  userData[buyer].inventory.push([fishSize, fishName]);

  // 판매 목록에서 제거
  delete market[seller][fishName + "-" + fishSize];
  if (Object.keys(market[seller]).length === 0) {
    delete market[seller];
  }

  saveData();
  replier.reply(
    buyer +
      " 님이 " +
      seller +
      " 님으로부터 " +
      fishSize +
      "cm 크기의 " +
      fishName +
      "를 " +
      price +
      " 골드에 구매했습니다."
  );
}

function checkMarket(replier) {
  if (Object.keys(market).length === 0) {
    replier.reply("현재 시장에 등록된 물고기가 없습니다.");
    return;
  }

  let marketList = "현재 수산시장에 등록된 물고기 목록:\n";
  for (let seller in market) {
    marketList += seller + "님의 물고기:\n";
    for (let fish in market[seller]) {
      marketList += " - " + fish + ": " + market[seller][fish] + "골드\n";
    }
  }
  replier.reply(marketList);
}

function checkGold(sender, replier) {
  checkUser(sender); // 사용자가 존재하는지 확인하고 없으면 생성
  if (userData[sender] && typeof userData[sender].gold === "number") {
    const currentGold = userData[sender].gold; // 사용자 골드 불러오기
    replier.reply(sender + " 님의 현재 골드는 " + currentGold + " 골드입니다.");
  } else {
    // 골드가 초기화되지 않았을 경우 초기화
    userData[sender].gold = 10000;
    saveData(); // 데이터를 저장하여 파일에 기록
    replier.reply(
      sender + " 님의 현재 골드는 " + userData[sender].gold + " 골드입니다."
    );
  }
}

function issueGold(targetUser, amount, replier) {
  checkUser(targetUser); // 사용자가 존재하는지 확인하고 없으면 생성
  userData[targetUser].gold += amount; // 해당 사용자에게 골드 추가
  saveData(); // 변경 사항 저장
  replier.reply(
    targetUser +
      " 님에게 " +
      amount +
      " 골드가 발행되었습니다. 현재 골드: " +
      userData[targetUser].gold
  );
}

// /로또구매 [금액] /로또확인 /로또추첨 (24시간마다 사용 할 수 있고 1명 당첨되며 당첨금은 1억원 입니다)
// /로또랭킹 (로또 구매 금액 순위)
// 로또 관련 데이터는 const PATH = "./userData.txt"; 에 저장되어 있습니다.

const lottoData = {
  pot: 0, // 현재 로또 총 당첨금
  tickets: {}, // 사용자별 구매한 로또
  lastWinner: null, // 마지막 당첨자
  lastDrawTime: null, // 마지막 추첨 시간
};

function saveLottoData() {
  userData.lotto = lottoData;
  saveData();
}

function loadLottoData() {
  if (userData.lotto) {
    Object.assign(lottoData, userData.lotto);
  }
}

function buyLotto(sender, amount, replier) {
  checkUser(sender);

  if (amount <= 0 || isNaN(amount)) {
    replier.reply("올바른 금액을 입력해주세요. 예: /로또구매 [금액]");
    return;
  }

  if (userData[sender].gold < amount) {
    replier.reply(sender + " 님의 골드가 부족합니다.");
    return;
  }

  // 로또 구매 처리
  userData[sender].gold -= amount;
  lottoData.pot += amount;

  if (!lottoData.tickets[sender]) {
    lottoData.tickets[sender] = 0;
  }
  lottoData.tickets[sender] += amount;

  saveLottoData();
  replier.reply(sender + " 님이 " + amount + " 골드로 로또를 구매했습니다!");
}

function checkLotto(sender, replier) {
  checkUser(sender);

  const tickets = lottoData.tickets[sender] || 0;
  const pot = lottoData.pot;
  const lastWinner = lottoData.lastWinner
    ? lottoData.lastWinner + " 님이 " + lottoData.pot + " 골드를 받았습니다."
    : "아직 당첨자가 없습니다.";

  replier.reply(
    sender +
      " 님의 로또 구매 금액: " +
      tickets +
      "골드\n" +
      "현재 로또 당첨금: " +
      pot +
      " 골드\n" +
      "최근 당첨자: " +
      lastWinner
  );
}

function drawLotto(replier) {
  const currentTime = new Date();
  if (
    lottoData.lastDrawTime &&
    currentTime - new Date(lottoData.lastDrawTime) < 24 * 60 * 60 * 1000
  ) {
    replier.reply("로또는 하루에 한 번만 추첨할 수 있습니다.");
    return;
  }

  const players = Object.keys(lottoData.tickets).filter(
    (player) => lottoData.tickets[player] > 0
  );

  if (players.length === 0) {
    replier.reply("참여한 사용자가 없어 로또를 추첨할 수 없습니다.");
    return;
  }

  // 당첨자 선정
  const winner = players[Math.floor(Math.random() * players.length)];
  const winnings = lottoData.pot;

  // 상금 지급 및 데이터 초기화
  userData[winner].gold += winnings;
  lottoData.lastWinner = winner;
  lottoData.pot = 0;
  lottoData.tickets = {};
  lottoData.lastDrawTime = currentTime.toISOString();

  saveLottoData();
  saveData();

  replier.reply(
    "🎉 로또 당첨자: " +
      winner +
      " 님! 🎉\n" +
      "당첨금: " +
      winnings +
      "골드\n" +
      +winner +
      " 님께 골드가 지급되었습니다!"
  );
}

function lottoRanking(replier) {
  const ranking = Object.entries(lottoData.tickets)
    .sort(([, a], [, b]) => b - a)
    .map(([user, amount], index) => `${index + 1}위: ${user} (${amount} 골드)`);

  replier.reply(
    "🎰 로또 구매 금액 순위 🎰\n" +
      (ranking.length > 0 ? ranking.join("\n") : "현재 구매자가 없습니다.")
  );
}

function processLottoPurchase(msg, sender, replier) {
  const parts = msg.split(" ");
  if (parts.length === 2) {
    const amount = parseInt(parts[1]);
    if (!isNaN(amount)) {
      buyLotto(sender, amount, replier);
    } else {
      replier.reply("올바른 금액을 입력해주세요. 예: /로또구매 [금액]");
    }
  } else {
    replier.reply("올바른 형식으로 입력해주세요. 예: /로또구매 [금액]");
  }
}

/* 메시지 처리에 추가
  if (msg.indexOf("/로또구매 ") === 0) {
    const parts = msg.split(" ");
    if (parts.length === 2) {
      const amount = parseInt(parts[1]);
      if (!isNaN(amount)) {
        buyLotto(sender, amount, replier, msg);
      } else {
        replier.reply("올바른 금액을 입력해주세요. 예: /로또구매 [금액]");
      }
    } else {
      replier.reply("올바른 형식으로 입력해주세요. 예: /로또구매 [금액]");
    }
  } */

// 메시지 처리 함수

// 초기 데이터 로드
loadLottoData();
profile
IT를 좋아합니다.

0개의 댓글