{ }codelper
메서드P0346

제 상태를 문자열로 말하는 게임 캐릭터

게임 캐릭터를 클래스로 만들어라. 캐릭터 객체는 이름, 최대 체력 m, 지금 체력을 필드로 갖는다.
지금 체력은 m에서 시작한다. 캐릭터에는 메서드를 세 개 둔다. 맞기 hit(d)는 체력을 d만큼 줄인다.
다만 체력은 0보다 작아지지 않고 0에서 멈춘다. 회복 heal(h)는 체력을 h만큼 늘린다.
다만 체력은 m보다 커지지 않고 m에서 멈춘다. 체력이 0인 캐릭터는 쓰러진 것이라서, 회복을 받아도 아무것도 바뀌지 않는다. 상태 문자열 메서드는 캐릭터를 "이름 체력/m" 꼴의 문자열로 바꿔 돌려준다(예: ann 7/10).
체력이 0이면 그 뒤에 빈칸 하나와 down을 붙인다(예: ann 0/10 down). 상태 문자열 메서드는 그 언어가 객체를 문자열로 바꿀 때 부르는 이름으로 만들어 보자(Python __str__, Java·JS·TS toString, C# ToString, C++ operator<<). 첫 줄에 캐릭터 수 n과 명령 수 q가 주어진다. 다음 n줄에 1번부터 n번 캐릭터의 이름과 최대 체력 m이 한 줄에 하나씩 주어진다. 다음 q줄에 명령이 한 줄에 하나씩 주어진다. hit i d는 i번 캐릭터의 hit(d)를 부르고, heal i h는 i번 캐릭터의 heal(h)를 부른다. 명령을 하나 처리할 때마다 i번 캐릭터의 상태 문자열을 한 줄에 출력하라. 명령을 모두 처리한 뒤 마지막 줄에 쓰러지지 않은(체력이 1 이상인) 캐릭터의 수를 출력하라. 채점은 출력만 보므로 메서드를 어떻게 나눴는지는 검사하지 않는다.

입력
1 ≤ n ≤ 100, 1 ≤ q ≤ 1,000, 1 ≤ m ≤ 1,000, 1 ≤ i ≤ n, 1 ≤ d ≤ 1,000, 1 ≤ h ≤ 1,000(모두 정수이고 쉼표 없이 숫자만 주어진다).
이름은 영어 소문자·숫자 1~10글자이고, 같은 이름의 캐릭터가 여럿 있을 수 있다.
한 줄 안의 값은 빈칸 하나로 띄운다
출력
명령마다 한 줄씩 q줄, 그리고 마지막 줄에 쓰러지지 않은 캐릭터의 수
예시 1 · stdin
2 6
ann 10
bo 5
hit 1 3
heal 1 5
hit 2 9
heal 2 3
hit 1 4
hit 2 1
stdout
ann 7/10
ann 10/10
bo 0/5 down
bo 0/5 down
ann 6/10
bo 0/5 down
1
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 6개 언어 풀이

이 문제는 클래스와 메서드가 있는 Python·JavaScript·TypeScript·Java·C#·C++만 받아요(C에는 메서드가 없고, 크레이콘은 아직 클래스를 만들 수 없어요).
메서드는 객체가 가진 값을 바꾸거나(hit, heal) 그 값으로 답을 만들어 돌려주는(상태 문자열) 함수예요.
객체를 문자열로 바꾸는 메서드는 언어마다 정해진 이름이 있어요.
Python은 __str__을 만들면 print(hero)·str(hero)가 그것을 불러요.
Java는 toString()을 만들면 System.out.println(hero)와 "" + hero가, C#은 override string ToString()을 만들면 Console.WriteLine(hero)와 $"{hero}"가 불러요.
JS·TS는 toString()을 만들면 ` ${hero} ·String(hero)·"" + hero`가 불러요.
C++에는 그런 메서드가 없어서 operator<<를 만들어 cout << hero가 되게 해요.
함정 하나: JS의 console.log(hero)는 toString을 부르지 않고 객체의 속을 보여 줘서, 예시의 첫 줄이 Hero { name: 'ann', maxHp: 10, hp: 7 }이 돼요.
함정 둘: C#에서 override를 빼고 public string ToString()이라 쓰면 경고만 나오고 컴파일돼요.
그런데 StringBuilder.Append(hero)는 원래의 object.ToString()을 불러서 줄마다 클래스 이름 Hero만 나와요.
함정 셋: Java에서 이름을 tostring()처럼 다르게 쓰면 그냥 새 메서드가 되어서 Main$Hero@55f96302 같은 문자열이 나와요.
메서드 위에 @Override를 붙여 두면 이런 오타를 컴파일 오류로 알려 줘요.
함정 넷: 체력을 0과 m 사이에 가두는 일은 메서드 안에서 해요.
hp - d를 그대로 넣으면 예시의 셋째 줄이 bo -4/5가 되고, hp + h를 그대로 넣으면 둘째 줄이 ann 12/10이 돼요.
Python은 max(0, …)·min(m, …), JS·TS·Java는 Math.max·Math.min, C#은 Math.Max·Math.Min, C++은 std::max·std::min을 써요.
함정 다섯: 쓰러졌는지는 heal 안에서 먼저 물어야 해요.
묻지 않으면 예시의 넷째 줄이 bo 3/5가 되어 쓰러진 캐릭터가 다시 일어나요.

Pythonmain.py · 47 줄
import sys  class Hero:    def __init__(self, name, max_hp):        self.name = name        self.max_hp = max_hp        self.hp = max_hp  # 체력은 가득 찬 채로 시작한다     def hit(self, d):        self.hp = max(0, self.hp - d)  # 0보다 작아지지 않는다     def heal(self, h):        if self.hp == 0:  # 쓰러진 캐릭터는 회복하지 못한다            return        self.hp = min(self.max_hp, self.hp + h)     def is_down(self):        return self.hp == 0     # print(hero)·str(hero)·f"{hero}"가 부르는 메서드    def __str__(self):        text = f"{self.name} {self.hp}/{self.max_hp}"        return text + " down" if self.is_down() else text  def main():    lines = sys.stdin.read().split("\n")    n, q = map(int, lines[0].split())    heroes = []    for i in range(1, n + 1):        name, m = lines[i].split()        heroes.append(Hero(name, int(m)))    out = []    for i in range(n + 1, n + 1 + q):        cmd, idx, amount = lines[i].split()        hero = heroes[int(idx) - 1]        if cmd == "hit":            hero.hit(int(amount))        else:            hero.heal(int(amount))        out.append(str(hero))    out.append(str(sum(1 for h in heroes if not h.is_down())))    print("\n".join(out))  main()
JavaScriptmain.js · 44 줄
class Hero {  constructor(name, maxHp) {    this.name = name;    this.maxHp = maxHp;    this.hp = maxHp; // 체력은 가득 찬 채로 시작한다  }   hit(d) {    this.hp = Math.max(0, this.hp - d); // 0보다 작아지지 않는다  }   heal(h) {    if (this.hp === 0) return; // 쓰러진 캐릭터는 회복하지 못한다    this.hp = Math.min(this.maxHp, this.hp + h);  }   isDown() {    return this.hp === 0;  }   // `${hero}`·String(hero)·"" + hero 가 부르는 메서드  toString() {    const text = `${this.name} ${this.hp}/${this.maxHp}`;    return this.isDown() ? text + " down" : text;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const heroes = [];for (let i = 1; i <= n; i++) {  const [name, m] = lines[i].trim().split(" ");  heroes.push(new Hero(name, Number(m)));}const out = [];for (let i = n + 1; i <= n + q; i++) {  const [cmd, idx, amount] = lines[i].trim().split(" ");  const hero = heroes[Number(idx) - 1];  if (cmd === "hit") hero.hit(Number(amount));  else hero.heal(Number(amount));  out.push(`${hero}`); // console.log(hero)는 toString을 부르지 않는다}out.push(String(heroes.filter((h) => !h.isDown()).length));console.log(out.join("\n"));
TypeScriptmain.ts · 46 줄
import { readFileSync } from "node:fs"; class Hero {  private hp: number;   constructor(private readonly name: string, private readonly maxHp: number) {    this.hp = maxHp; // 체력은 가득 찬 채로 시작한다  }   hit(d: number): void {    this.hp = Math.max(0, this.hp - d); // 0보다 작아지지 않는다  }   heal(h: number): void {    if (this.hp === 0) return; // 쓰러진 캐릭터는 회복하지 못한다    this.hp = Math.min(this.maxHp, this.hp + h);  }   isDown(): boolean {    return this.hp === 0;  }   // `${hero}`·String(hero)가 부르는 메서드  toString(): string {    const text = `${this.name} ${this.hp}/${this.maxHp}`;    return this.isDown() ? text + " down" : text;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const heroes: Hero[] = [];for (let i = 1; i <= n; i++) {  const [name, m] = lines[i].trim().split(" ");  heroes.push(new Hero(name, Number(m)));}const out: string[] = [];for (let i = n + 1; i <= n + q; i++) {  const [cmd, idx, amount] = lines[i].trim().split(" ");  const hero = heroes[Number(idx) - 1];  if (cmd === "hit") hero.hit(Number(amount));  else hero.heal(Number(amount));  out.push(`${hero}`); // console.log(hero)는 toString을 부르지 않는다}out.push(String(heroes.filter((h) => !h.isDown()).length));console.log(out.join("\n"));
JavaMain.java · 64 줄
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    static class Hero {        private final String name;        private final int maxHp;        private int hp;         Hero(String name, int maxHp) {            this.name = name;            this.maxHp = maxHp;            this.hp = maxHp; // 체력은 가득 찬 채로 시작한다        }         void hit(int d) {            hp = Math.max(0, hp - d); // 0보다 작아지지 않는다        }         void heal(int h) {            if (hp == 0) return; // 쓰러진 캐릭터는 회복하지 못한다            hp = Math.min(maxHp, hp + h);        }         boolean isDown() {            return hp == 0;        }         // println(hero)와 "" + hero 가 부르는 메서드. @Override가 이름 오타를 잡아 준다        @Override        public String toString() {            String text = name + " " + hp + "/" + maxHp;            return isDown() ? text + " down" : text;        }    }     public static void main(String[] args) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int n = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        Hero[] heroes = new Hero[n];        for (int i = 0; i < n; i++) {            st = new StringTokenizer(br.readLine());            String name = st.nextToken();            heroes[i] = new Hero(name, Integer.parseInt(st.nextToken()));        }        StringBuilder sb = new StringBuilder();        for (int i = 0; i < q; i++) {            st = new StringTokenizer(br.readLine());            String cmd = st.nextToken();            Hero hero = heroes[Integer.parseInt(st.nextToken()) - 1];            int amount = Integer.parseInt(st.nextToken());            if (cmd.equals("hit")) hero.hit(amount);            else hero.heal(amount);            sb.append(hero).append('\n'); // append(객체)가 toString을 부른다        }        int up = 0;        for (Hero h : heroes) if (!h.isDown()) up++;        sb.append(up).append('\n');        System.out.print(sb);    }}
C#Main.cs · 65 줄
using System;using System.Text; class Hero{    private readonly string name;    private readonly int maxHp;    private int hp;     public Hero(string name, int maxHp)    {        this.name = name;        this.maxHp = maxHp;        hp = maxHp; // 체력은 가득 찬 채로 시작한다    }     public void Hit(int d)    {        hp = Math.Max(0, hp - d); // 0보다 작아지지 않는다    }     public void Heal(int h)    {        if (hp == 0) return; // 쓰러진 캐릭터는 회복하지 못한다        hp = Math.Min(maxHp, hp + h);    }     public bool IsDown => hp == 0;     // Console.WriteLine(hero)와 $"{hero}"가 부르는 메서드. override를 빼면 불리지 않는다    public override string ToString()    {        string text = $"{name} {hp}/{maxHp}";        return IsDown ? text + " down" : text;    }} class Program{    static void Main()    {        string[] first = Console.ReadLine()!.Trim().Split(' ');        int n = int.Parse(first[0]), q = int.Parse(first[1]);        var heroes = new Hero[n];        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            heroes[i] = new Hero(p[0], int.Parse(p[1]));        }        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            Hero hero = heroes[int.Parse(p[1]) - 1];            int amount = int.Parse(p[2]);            if (p[0] == "hit") hero.Hit(amount);            else hero.Heal(amount);            sb.Append(hero).Append('\n'); // Append(객체)가 ToString을 부른다        }        int up = 0;        foreach (Hero h in heroes) if (!h.IsDown) up++;        sb.Append(up).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 58 줄
#include <algorithm>#include <iostream>#include <string>#include <vector>using namespace std; class Hero {    string name_;    int max_hp_;    int hp_; public:    Hero(const string& name, int max_hp) : name_(name), max_hp_(max_hp), hp_(max_hp) {}     void hit(int d) { hp_ = max(0, hp_ - d); }  // 0보다 작아지지 않는다     void heal(int h) {        if (hp_ == 0) return;  // 쓰러진 캐릭터는 회복하지 못한다        hp_ = min(max_hp_, hp_ + h);    }     bool is_down() const { return hp_ == 0; }     // C++에는 toString이 없다. cout << hero 가 되도록 << 연산자를 만들어 준다    friend ostream& operator<<(ostream& os, const Hero& h) {        os << h.name_ << ' ' << h.hp_ << '/' << h.max_hp_;        if (h.is_down()) os << " down";        return os;    }}; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, q;    cin >> n >> q;    vector<Hero> heroes;    for (int i = 0; i < n; i++) {        string name;        int m;        cin >> name >> m;        heroes.emplace_back(name, m);    }    for (int i = 0; i < q; i++) {        string cmd;        int idx, amount;        cin >> cmd >> idx >> amount;        Hero& hero = heroes[idx - 1];  // &가 없으면 복사본을 고치게 된다        if (cmd == "hit") hero.hit(amount);        else hero.heal(amount);        cout << hero << '\n';    }    int up = 0;    for (const Hero& h : heroes)        if (!h.is_down()) up++;    cout << up << '\n';    return 0;}