きょうは、くらすで大乱闘した。
問題概要
Paizaの格ゲー問題は、プレイヤーが強化技 or 攻撃技を選んで殴り合うだけ!そして、最後まで生き残ってる人数を出力!
ポイントは、
- 技は3種類:発生フレームと攻撃力を持つ
- どちらかが強化技なら相手の攻撃を食らって自分はバフ
- どっちも攻撃技ならフレーム勝負で速い方が当たる
- HP0で退場
N K
hp_1 F1_1 A1_1 F2_1 A2_1 F3_1 A3_1
...
hp_N F1_N A1_N F2_N A2_N F3_N A3_N
P1_1 T1_1 P2_1 T2_1
...
P1_K T1_K P2_K T2_K
- プレイヤー数 N と攻撃回数 K
- F → フレーム、 A → 攻撃力
- 0 0 は強化技
入力例:
3 6
10 1 1 2 2 3 3
10 0 0 6 1 7 2
10 0 0 7 5 8 3
1 1 2 2
1 2 3 2
1 3 2 3
2 2 3 1
2 3 3 1
1 2 3 2
出力例:
2
✅ OKコード例:
// ------------------------------
// Player クラス定義
// ------------------------------
class Player {
constructor(hp, f1, a1, f2, a2, f3, a3) {
this.hp = hp;
// f: 技の発生フレーム、a: 攻撃力 を配列で管理
this.f = [f1, f2, f3];
this.a = [a1, a2, a3];
}
// 強化技の処理: 他の全技を強化する
enhance() {
for (let i = 0; i < 3; i++) {
// これは強化技自身(f=0, a=0)なら無視
if (this.f[i] === 0 && this.a[i] === 0) continue;
// 発生フレーム -3 (最短 1) & 攻撃力 +5
this.a[i] += 5;
this.f[i] = Math.max(1, this.f[i] - 3);
}
}
// HPが正の値なら生存
isAlive() {
return this.hp > 0;
}
// ダメージを受ける
takeDamage(damage) {
this.hp -= damage;
}
}
// ------------------------------
// 標準入力 & メイン処理
// ------------------------------
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on('line', (line) => lines.push(line));
rl.on('close', () => {
// プレイヤー数 n & 攻撃回数 k
const [n, k] = lines[0].split(' ').map(Number);
// プレイヤー情報を読み込んでクラス生成
const players = [];
for (let i = 1; i <= n; i++) {
const [hp, f1, a1, f2, a2, f3, a3] = lines[i].split(' ').map(Number);
players.push(new Player(hp, f1, a1, f2, a2, f3, a3));
}
// K 回の攻撃処理
for (let i = n + 1; i < n + 1 + k; i++) {
// 各攻撃のプレイヤー番号 & 技番号(1-index なので -1)
const [p1Index, t1, p2Index, t2] = lines[i].split(' ').map(Number);
const p1 = players[p1Index - 1];
const p2 = players[p2Index - 1];
// 既にどちらかが退場していたら何も起きない
if (!p1.isAlive() || !p2.isAlive()) continue;
// 技の発生フレームと攻撃力を取り出す
const f1 = p1.f[t1 - 1];
const a1 = p1.a[t1 - 1];
const f2 = p2.f[t2 - 1];
const a2 = p2.a[t2 - 1];
// 強化技かどうかを判定(f=0,a=0 の場合だけ)
const p1Enhance = (f1 === 0 && a1 === 0);
const p2Enhance = (f2 === 0 && a2 === 0);
// ------------------------------
// 条件分岐
// ------------------------------
if (p1Enhance && p2Enhance) {
// 両方強化技: 互いに強化だけして終了
p1.enhance();
p2.enhance();
}
else if (p1Enhance) {
// p1 が強化技のみ: p1 は強化し、相手の攻撃を受ける
p1.enhance();
p1.takeDamage(a2);
}
else if (p2Enhance) {
// p2 が強化技のみ: p2 は強化し、相手の攻撃を受ける
p2.enhance();
p2.takeDamage(a1);
}
else {
// 両方攻撃技: 発生フレームを比較して速い方が攻撃を通す
if (f1 < f2) {
p2.takeDamage(a1); // p1 の攻撃が命中
}
else if (f1 > f2) {
p1.takeDamage(a2); // p2 の攻撃が命中
}
// f1 === f2 の場合は何も起こらない
}
}
// 生存者の数を数えて出力
const aliveCount = players.filter(p => p.isAlive()).length;
console.log(aliveCount);
});
✅ ポイント
- クラス内メソッドの意図
→enhance()
は「他の全技にバフをかける」
→isAlive()
とtakeDamage()
は自己管理。
- 攻撃条件の分岐でどう処理が分かれるか
→ 両方バフ → お互いバフだけ
→ 片方バフ → 相手の攻撃を食らう
→ 両方攻撃 → フレーム比較で速い方がヒット。
- インデックス調整
→ 入力値が 1-index だから -1 を必ず入れる。
※ 補足 Math.max(a, b)
Math.max(a, b)
は a
と b
のうち大きい方を返す JavaScript の組み込み関数。
🔍Math.max(1, f - 3) の意味
-
f - 3
を計算する。 - その結果と
1
を比べる。 - 2つのうち 大きい方 を返す。
つまり、フレームが 1 より小さくなったら 1 を返すので、最短フレーム 1 が担保される。
🗒️ まとめ:ロジックの流れ
1️⃣ プレイヤーの初期状態を読み込む
- 各プレイヤーは HP と 3 つの技(発生フレーム F と攻撃力 A)を持つ
- 技の F と A が両方 0 の場合は「強化技」
2️⃣ 攻撃の順に処理する
- 攻撃回数 K 回分の戦闘が順に与えられる
3️⃣ 戦闘ごとに以下を判定する
- 先に どちらかのプレイヤーがすでに退場 (HP <= 0) なら何もしない
- 選んだ技が強化技かを判定
-
F == 0 && A == 0
なら強化技
-
4️⃣ どちらも強化技の場合
- 両方のプレイヤーが同時に強化する(他の技を -3 フレーム& +5 ダメージ)
5️⃣ 片方だけ強化技の場合
- 強化技を使ったプレイヤーは自分の技を強化する
- 相手が攻撃技なら、その攻撃力分のダメージを受ける
6️⃣ 両方が攻撃技の場合
- 技の発生フレーム F を比較
- F1 < F2 → 相手(P2)が攻撃を受ける
- F1 > F2 → 自分(P1)が攻撃を受ける
- F1 == F2 → 何も起こらない
7️⃣ 強化効果の適用時
- 技のフレームを -3(ただし最短 1)
- 技のダメージを +5
8️⃣ すべての戦闘後
- HP > 0 のプレイヤーだけカウントして出力
🔑 ポイント
- 強化は何度も重複して使える(何回でも積み重ねOK)
- 技を出した直後に強化が即時反映される
- フレームは必ず 1 以上で止まる
- 攻撃は必ず片方だけが受ける(同時には受けない)