{ }codelper
classesP0261

Café stamp cards

Model a café stamp card as a class. A card object has the owner's name, the stamp goal g, the current number of stamps, and the number of coupons as fields. The constructor takes the owner's name and g; stamps and coupons start at 0. Each drink bought adds one stamp.
If the stamp count becomes g after stamping, it goes back to 0 and the card gains one coupon. The first line gives the number of commands q, and the next q lines give one command each. - new name g: make a new card for that name with goal g.
If a card with that name already exists, throw the old one away and replace it with the new card.
The new card has 0 stamps and 0 coupons.
No output. - buy name k: the owner of that card buys k drinks. - use name: if the card has a coupon, use one.
If it has none, change nothing. After each buy and use, print that card's "name stamps coupons" on one line, separated by single spaces. Try making one object per card, with stamping and using a coupon as methods.

Input
1 ≤ q ≤ 2,000.
Names are 1–10 lowercase letters or digits.
1 ≤ g ≤ 100, 1 ≤ k ≤ 100 (integers).
Names in buy and use belong to cards already made by an earlier new, and there is at least one buy or use
Output
One line per buy and use, in command order
Example 1 · stdin
8
new ann 3
buy ann 2
buy ann 5
use ann
new bob 1
buy bob 2
use ann
use ann
stdout
ann 2 0
ann 1 2
ann 1 1
bob 0 2
ann 1 0
ann 1 0
Example 2 · stdin
5
new kim 2
buy kim 3
new kim 5
buy kim 1
use kim
stdout
kim 1 1
kim 1 0
kim 1 0
main.py
Input (stdin)

When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).

$ Ready

Open this on a computer to write and run code. On mobile you can read the problem and the solutions.

In other languagesShow solutions by language · 6 languages

This problem takes only the languages with classes: Python, JavaScript, TypeScript, Java, C# and C++ (C has no classes, and Craykon cannot define classes yet).
The constructor is Python __init__(self, owner, goal), JS/TS constructor, and in Java/C#/C++ a method named after the class.
In TypeScript, constructor(readonly owner: string, readonly goal: number) {} declares and assigns the fields in one go by putting readonly/private on the parameters.
C++ uses an initializer list : owner_(owner), goal_(goal).
Reaching a field differs too: Python always needs self., JS/TS always need this..
Java/C#/C++ let you omit this, but a parameter with the same name as a field hides the field.
Five traps: ① writing goal = goal; in a Java constructor just assigns the parameter to itself, so the field goal stays 0 and stamps never turn into coupons (this.goal = goal;).
If the field is final, the compiler catches it.
② in JS, passing a method on its own, as in forEach(this.stamp), loses this and throws a TypeError.
Wrap it in an arrow function () => this.stamp().
③ C++ std::map::emplace/insert do nothing when the key already exists, so a card re-made by new stays the old card.
insert_or_assign or cards[name] = ... replaces it (though [] needs a default constructor).
④ C++ StampCard card = cards.at(name); is a copy, so the stamps you add vanish.
Take StampCard& card.
In C#, making it a struct instead of a class gives a value type, so every read from the dictionary is a copy.
⑤ write the 'no coupon, no use' check once inside the use method, so no caller can drive the count to -1.

Pythonmain.py · 46 lines
import sys  class StampCard:    def __init__(self, owner, goal):        self.owner = owner        self.goal = goal        self.stamps = 0        self.coupons = 0     def stamp(self):        self.stamps += 1        if self.stamps == self.goal:  # 목표에 닿으면 쿠폰 한 장으로 바꾼다            self.stamps = 0            self.coupons += 1     def buy(self, k):        for _ in range(k):            self.stamp()     def use(self):        if self.coupons == 0:            return False        self.coupons -= 1        return True     def describe(self):        return f"{self.owner} {self.stamps} {self.coupons}"  lines = sys.stdin.read().split("\n")q = int(lines[0])cards = {}out = []for line in lines[1 : q + 1]:    p = line.split()    if p[0] == "new":        cards[p[1]] = StampCard(p[1], int(p[2]))  # 이미 있으면 새 카드로 바뀐다    else:        card = cards[p[1]]        if p[0] == "buy":            card.buy(int(p[2]))        else:            card.use()        out.append(card.describe())print("\n".join(out))
JavaScriptmain.js · 48 lines
class StampCard {  constructor(owner, goal) {    this.owner = owner;    this.goal = goal;    this.stamps = 0;    this.coupons = 0;  }   stamp() {    this.stamps++;    if (this.stamps === this.goal) {      // 목표에 닿으면 쿠폰 한 장으로 바꾼다      this.stamps = 0;      this.coupons++;    }  }   buy(k) {    for (let i = 0; i < k; i++) this.stamp();  }   use() {    if (this.coupons === 0) return false;    this.coupons--;    return true;  }   describe() {    return `${this.owner} ${this.stamps} ${this.coupons}`;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const cards = new Map();const out = [];for (let i = 1; i <= q; i++) {  const [cmd, name, num] = lines[i].trim().split(" ");  if (cmd === "new") {    cards.set(name, new StampCard(name, Number(num))); // 이미 있으면 새 카드로 바뀐다    continue;  }  const card = cards.get(name);  if (cmd === "buy") card.buy(Number(num));  else card.use();  out.push(card.describe());}console.log(out.join("\n"));
TypeScriptmain.ts · 49 lines
import { readFileSync } from "node:fs"; class StampCard {  private stamps = 0;  private coupons = 0;   // 매개변수 앞의 readonly가 같은 이름의 필드를 만들고 값을 넣어 준다  constructor(readonly owner: string, readonly goal: number) {}   private stamp(): void {    this.stamps++;    if (this.stamps === this.goal) {      // 목표에 닿으면 쿠폰 한 장으로 바꾼다      this.stamps = 0;      this.coupons++;    }  }   buy(k: number): void {    for (let i = 0; i < k; i++) this.stamp();  }   use(): boolean {    if (this.coupons === 0) return false;    this.coupons--;    return true;  }   describe(): string {    return `${this.owner} ${this.stamps} ${this.coupons}`;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[0]);const cards = new Map<string, StampCard>();const out: string[] = [];for (let i = 1; i <= q; i++) {  const [cmd, name, num] = lines[i].trim().split(" ");  if (cmd === "new") {    cards.set(name, new StampCard(name, Number(num))); // 이미 있으면 새 카드로 바뀐다    continue;  }  const card = cards.get(name)!;  if (cmd === "buy") card.buy(Number(num));  else card.use();  out.push(card.describe());}console.log(out.join("\n"));
JavaMain.java · 59 lines
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map; class StampCard {    private final String owner;    private final int goal;    private int stamps = 0;    private int coupons = 0;     StampCard(String owner, int goal) {        this.owner = owner; // this. 가 없으면 매개변수에 자기 자신을 넣을 뿐이다        this.goal = goal;    }     private void stamp() {        stamps++;        if (stamps == goal) { // 목표에 닿으면 쿠폰 한 장으로 바꾼다            stamps = 0;            coupons++;        }    }     void buy(int k) {        for (int i = 0; i < k; i++) stamp();    }     boolean use() {        if (coupons == 0) return false;        coupons--;        return true;    }     String describe() {        return owner + " " + stamps + " " + coupons;    }} public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        Map<String, StampCard> cards = new HashMap<>();        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            String[] p = in.readLine().trim().split(" ");            if (p[0].equals("new")) {                cards.put(p[1], new StampCard(p[1], Integer.parseInt(p[2]))); // 이미 있으면 새 카드로 바뀐다                continue;            }            StampCard card = cards.get(p[1]);            if (p[0].equals("buy")) card.buy(Integer.parseInt(p[2]));            else card.use();            out.append(card.describe()).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 65 lines
using System;using System.Collections.Generic;using System.Text; class StampCard // struct로 만들면 사전에서 꺼낼 때마다 복사본이 나온다{    public string Owner { get; }    public int Goal { get; }    public int Stamps { get; private set; }    public int Coupons { get; private set; }     public StampCard(string owner, int goal)    {        Owner = owner;        Goal = goal;    }     private void Stamp()    {        Stamps++;        if (Stamps == Goal) // 목표에 닿으면 쿠폰 한 장으로 바꾼다        {            Stamps = 0;            Coupons++;        }    }     public void Buy(int k)    {        for (int i = 0; i < k; i++) Stamp();    }     public bool Use()    {        if (Coupons == 0) return false;        Coupons--;        return true;    }     public override string ToString() => $"{Owner} {Stamps} {Coupons}";} class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine()!.Trim());        var cards = new Dictionary<string, StampCard>();        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            if (p[0] == "new")            {                cards[p[1]] = new StampCard(p[1], int.Parse(p[2])); // 이미 있으면 새 카드로 바뀐다                continue;            }            StampCard card = cards[p[1]];            if (p[0] == "buy") card.Buy(int.Parse(p[2]));            else card.Use();            sb.Append(card).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 62 lines
#include <iostream>#include <map>#include <string> class StampCard {public:    StampCard(std::string owner, int goal) : owner_(std::move(owner)), goal_(goal) {}     void buy(int k) {        for (int i = 0; i < k; i++) stamp();    }     bool use() {        if (coupons_ == 0) return false;        coupons_--;        return true;    }     std::string describe() const {        return owner_ + " " + std::to_string(stamps_) + " " + std::to_string(coupons_);    } private:    void stamp() {        stamps_++;        if (stamps_ == goal_) { // 목표에 닿으면 쿠폰 한 장으로 바꾼다            stamps_ = 0;            coupons_++;        }    }     std::string owner_;    int goal_;    int stamps_ = 0;    int coupons_ = 0;}; int main() {    std::ios::sync_with_stdio(false);    int q;    std::cin >> q;    std::map<std::string, StampCard> cards;    for (int i = 0; i < q; i++) {        std::string cmd, name;        std::cin >> cmd >> name;        if (cmd == "new") {            int g;            std::cin >> g;            cards.insert_or_assign(name, StampCard(name, g)); // emplace·insert는 이미 있으면 바꾸지 않는다            continue;        }        StampCard& card = cards.at(name); // &가 없으면 복사본을 고친다        if (cmd == "buy") {            int k;            std::cin >> k;            card.buy(k);        } else {            card.use();        }        std::cout << card.describe() << '\n';    }}