[paiza]めくりめくり（神経衰弱（オリジナル） (paizaランク B 相当)

# 一人での限界
いつもふざけたことばかり書いていますが、
一応、会社の一業務としてQiitaへの投稿をしてます。
完全に人選ミスですねw
最近paizaのコラボがあるお陰でネタに困らないですが、ほぼ一人で会社の顔として何かを発信するってなかなかしんどみの極み。
そろそろ書く人を増やしてもらって、[QiitaのOrganization](https://qiita.com/organizations)で1位取ってみたいところです。
※やるからには1番取ってみたいよね（<ruby>頂上<rt>てっぺん</rt></ruby>獲るっショ

https://paiza.jp/works/mondai/b_rank_skillcheck_archive/concentration


# チェック処理とかは端折ってる
/** ここから定型文 */
ここで記載したクラスについては、
[こっちの記事](https://qiita.com/AsilHatake/items/40204293bdf59cb9f9d8)に書いてある、標準入力をList化したりしてるMainクラスから呼び出す予定だし・なんか継承したりしてるYO!!

ちな、Mainを実行して標準入力から入力するのクソ面倒くさいので、
Java編については問題の入力例をパラメータにしたテストクラスとかも作って公開するYO!!

<details open><summary>開発・実行環境はこんな感じ</summary>

- VSCode
- Java 17
- jUnit 5.9
- maven

pom.xmlはこんな感じ

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jp.co.asil</groupId>
    <artifactId>paiza202408</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
```

</details>
/** ここまで定型文 */

## 実装クラス 
```java:Concentration.java
package jp.co.asil.paiza202408;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Concentration extends Question {

  public Concentration(List<String> list) {
    super(list);
  }

  /**
   * 神経衰弱と呼ばれるトランプゲームのシミュレーションをしましょう。
   * 今回は数字が書かれたトランプのみを考え、ジョーカーは考えません。
   * 
   * まず、トランプを縦 H 枚、横 W 枚の長方形の形に並べた状態でスタートします。
   * H × W 枚のトランプには 1 〜 13 の数字のうちどれか1つが書かれています。
   * また、同じ数字が書かれたトランプが複数あります。
   * 
   * プレイヤーが N 人おり、それぞれ 1 〜 N で番号付けられています。
   * ゲームが始まると、1番の人から、このような手順でプレイしていきます。
   * 
   * ・並べられたトランプから2枚のトランプを選び、めくります。
   * ・めくった2枚のトランプに異なる数字が書かれていれば、次のプレイヤーの手番となります。同じ数字であれば、次の操作をおこないます。
   * ・まず、2枚のトランプはめくったプレーヤーのものとなり、取り除かれます。
   * ・トランプがすべて取り除かれた場合、ゲームは終了となります。
   * ・トランプが残っている場合、同じプレーヤーがまた最初の手順に戻り、トランプをめくります。
   * 
   * ここで、N 番のプレイヤーの次のプレイヤーは 1 番のプレイヤーであるとします。
   * 
   * ゲームの初期状態におけるトランプの配置と、ゲームが終わるまでに捲られたトランプに関する時系列順の記録が与えられます。
   * その記録を用いて、各プレイヤーが取り除いたトランプの枚数を求めてください。
   */
  @Override
  public List<String> answer() {
    // 条件取得(0:row, 1:col, 2:player)
    int[] cond = List.of(list.get(0).split(" ")).stream().mapToInt(Integer::parseInt).toArray();

    // カード情報を保持するMap(key:カード位置情報(row_col), value:カード情報)
    Map<String, Card> cardMap = new HashMap<>();
    // カード情報の設定
    for (int i = 1; i <= cond[0]; i++) {
      int[] rowItems = List.of(list.get(i).split(" ")).stream().mapToInt(Integer::parseInt).toArray();
      for (int j = 0; j < cond[1]; j++) {
        cardMap.put(i + "_" + (j + 1), new Card(rowItems[j]));
      }
    }

    // 人数分の取得数保持用変数
    int[] player = new int[cond[2]];
    int[] playerIndex = { 0 };

    // ログから取得状況を確認
    list.subList(cond[0] + 2, list.size()).forEach(log -> {
      int[] logItem = List.of(log.split(" ")).stream().mapToInt(Integer::parseInt).toArray();
      String[] logKey = { logItem[0] + "_" + logItem[1], logItem[2] + "_" + logItem[3] };
      // カードが選択されているかどうかをチェック
      Card[] card = {
          cardMap.get(logKey[0]),
          cardMap.get(logKey[1])
      };
      if (card[0].no == card[1].no && !card[0].isSelected && !card[1].isSelected) {
        // 合ってたら、カードを選択状態にして、持ちカード数をカウントアップする
        card[0].isSelected = true;
        card[1].isSelected = true;
        player[playerIndex[0]] += 2;
      } else {
        // 間違ってたらプレイヤーチェンジ
        playerIndex[0]++;
        playerIndex[0] = playerIndex[0] == cond[2] ? 0 : playerIndex[0];
      }
    });
    return Arrays.stream(player).mapToObj(String::valueOf).collect(Collectors.toList());
  }

  class Card {
    int no;
    boolean isSelected;

    public Card(int no) {
      this.no = no;
      this.isSelected = false;
    }
  }

}
```

## テストクラス 
```java:ConcentrationTest.java
package jp.co.asil.paiza202408;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;

import java.util.List;

import org.junit.jupiter.api.Test;

public class ConcentrationTest {
  @Test
  void testAnswer1() {
    Concentration testClass = new Concentration(List.of("2 3 2",
        "1 2 3",
        "2 1 3",
        "5",
        "1 1 2 1",
        "1 1 1 2",
        "1 1 2 2",
        "1 3 2 3",
        "1 2 2 1"));
    assertArrayEquals(testClass.answer().toArray(), new String[] { "6",
        "0" });
  }

  @Test
  void testAnswer2() {
    Concentration testClass = new Concentration(List.of("2 5 3",
        "5 8 8 6 3",
        "3 6 3 3 5",
        "8",
        "1 4 2 2",
        "1 3 2 1",
        "2 4 2 3",
        "1 3 1 5",
        "2 5 1 1",
        "2 1 1 2",
        "1 5 2 1",
        "1 2 1 3"));
    assertArrayEquals(testClass.answer().toArray(), new String[] { "6",
        "2",
        "2" });
  }
}
```

# 凡ミスにハマる
最後のint[]のplayerを返すところ、
`List.of(player).stream().map(String::valueOf).collect(Collectors.toList());`
って最初実装してしまい、最終的に返る値がint[]をToStringしたものになってしまいテストクラスでエラー吐きまくり。。。

配列のStream化は`Arrays.stream(配列変数)`でやろうねっていう話でした。