From 620e3d68109326679e740defd0aa133ff3c8138c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=99=E9=8D=8B=20=E5=B0=9A=E8=BC=9D?= Date: Thu, 24 Jul 2025 17:01:13 +0900 Subject: [PATCH 1/2] =?UTF-8?q?paiza=E3=81=AE=E5=95=8F=E9=A1=8CB=5F138?= =?UTF-8?q?=E3=81=AE=E5=9B=9E=E7=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ntakanabe/src/b138/B138.java | 27 ++ ntakanabe/src/b138/B138Test.java | 124 ++++++++ ntakanabe/src/b138/DonutCounterExecutor.java | 41 +++ .../src/b138/DonutCounterExecutorTest.java | 45 +++ ntakanabe/src/b138/DonutProcessor.java | 106 +++++++ ntakanabe/src/b138/DonutProcessorTest.java | 291 ++++++++++++++++++ ntakanabe/src/b138/GroundPictureData.java | 51 +++ ntakanabe/src/b138/GroundPictureDataTest.java | 24 ++ ntakanabe/src/b138/InputGroundPicture.java | 34 ++ .../src/b138/InputGroundPictureTest.java | 101 ++++++ ntakanabe/src/b138/OutputResult.java | 29 ++ ntakanabe/src/b138/OutputResultTest.java | 61 ++++ 12 files changed, 934 insertions(+) create mode 100644 ntakanabe/src/b138/B138.java create mode 100644 ntakanabe/src/b138/B138Test.java create mode 100644 ntakanabe/src/b138/DonutCounterExecutor.java create mode 100644 ntakanabe/src/b138/DonutCounterExecutorTest.java create mode 100644 ntakanabe/src/b138/DonutProcessor.java create mode 100644 ntakanabe/src/b138/DonutProcessorTest.java create mode 100644 ntakanabe/src/b138/GroundPictureData.java create mode 100644 ntakanabe/src/b138/GroundPictureDataTest.java create mode 100644 ntakanabe/src/b138/InputGroundPicture.java create mode 100644 ntakanabe/src/b138/InputGroundPictureTest.java create mode 100644 ntakanabe/src/b138/OutputResult.java create mode 100644 ntakanabe/src/b138/OutputResultTest.java diff --git a/ntakanabe/src/b138/B138.java b/ntakanabe/src/b138/B138.java new file mode 100644 index 0000000..8cc6315 --- /dev/null +++ b/ntakanabe/src/b138/B138.java @@ -0,0 +1,27 @@ +package paiza.product.b138; + +import java.util.Scanner; + +/** + * B138 mainメソッドをもつメインクラス. 与えられた地上絵の模様に、「ドーナツの絵」がいくつ含まれているかを数えるプログラム. + * + * @author ntakanabe + */ +public class B138 { + + /** + * 入力、処理、出力クラス、メソッドを呼び出すmainメソッド. + * + * @param args コマンドライン引数 + */ + public static void main(final String[] args) { + try (Scanner sc = new Scanner(System.in)) { + final DonutCounterExecutor sut = new DonutCounterExecutor(new InputGroundPicture( + sc), + new DonutProcessor(), + new OutputResult(System.out)); + + sut.execute(); + } + } +} diff --git a/ntakanabe/src/b138/B138Test.java b/ntakanabe/src/b138/B138Test.java new file mode 100644 index 0000000..a1973f8 --- /dev/null +++ b/ntakanabe/src/b138/B138Test.java @@ -0,0 +1,124 @@ +package paiza.test.b138; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.NoSuchElementException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import paiza.product.b138.B138; + +@RunWith(MockitoJUnitRunner.class) +public class B138Test { + + private ByteArrayOutputStream outputStreamCaptor; + private InputStream originalIn; + private PrintStream originalOut; + + @Before + public void setUp() { + // 標準出力をキャプチャするための設定 + outputStreamCaptor = new ByteArrayOutputStream(); + originalOut = System.out; + System.setOut(new PrintStream(outputStreamCaptor)); + originalIn = System.in; + } + + @After + public void tearDown() { + // テスト後に標準出力を元に戻す + System.setOut(originalOut); + System.setIn(originalIn); + } + + @Test + public void 地上絵にドーナツの絵が1つ含まれている場合1を返す() { + // テストデータ + final String input = "3 3\n" + + + "###\n" + + + "#.#\n" + + + "###\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + System.setIn(inputStream); + + B138.main(new String[] {}); + + assertThat(outputStreamCaptor.toString(), is(equalTo("1" + + System.lineSeparator()))); + } + + @Test + public void 地上絵にドーナツの絵が2つ含まれている場合2を返す() { + final String input = "3 7\n" + + + "###.###\n" + + + "#.#.#.#\n" + + + "###.###\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + System.setIn(inputStream); + + B138.main(new String[] {}); + + assertThat(outputStreamCaptor.toString(), is(equalTo("2" + + System.lineSeparator()))); + } + + @Test + public void 地上絵にドーナツの絵が含まれていない場合0を返す() { + final String input = "3 3\n" + + + "...\n" + + + ".#.\n" + + + "...\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + System.setIn(inputStream); + + B138.main(new String[] {}); + + assertThat(outputStreamCaptor.toString(), is(equalTo("0" + + System.lineSeparator()))); + } + + @Test + public void 地上絵のサイズが3_3より小さい場合0を返す() { + final String input = "2 2\n" + + + "##\n" + + + "##\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + System.setIn(inputStream); + + B138.main(new String[] {}); + + assertThat(outputStreamCaptor.toString(), is(equalTo("0" + + System.lineSeparator()))); + } + + @Test(expected = NoSuchElementException.class) + public void 地上絵の縦と横方向のマス数の入力後に改行していない場合例外がスローされる() { + // sc.nextLine()が呼び出されないケース + final String input = "3 3###\n" + + + "#.#\n" + + + "###\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + System.setIn(inputStream); + + B138.main(new String[] {}); + } +} diff --git a/ntakanabe/src/b138/DonutCounterExecutor.java b/ntakanabe/src/b138/DonutCounterExecutor.java new file mode 100644 index 0000000..38aa363 --- /dev/null +++ b/ntakanabe/src/b138/DonutCounterExecutor.java @@ -0,0 +1,41 @@ +package paiza.product.b138; + +/** + * ドーナツの絵のカウント処理を実行するクラス. + */ +public class DonutCounterExecutor { + + private final InputGroundPicture inputGroundPicture; + private final DonutProcessor donutProcessor; + private final OutputResult outputResult; + + /** + * DonutCounterExecutorの新しいインスタンスを初期化するコンストラクタ. + * + * @param inputGroundPicture 入力処理クラス + * @param donutProcessor ドーナツカウント処理クラス + * @param outputResult 出力処理クラス + */ + public DonutCounterExecutor(final InputGroundPicture inputGroundPicture, + final DonutProcessor donutProcessor, + final OutputResult outputResult) { + this.inputGroundPicture = inputGroundPicture; + this.donutProcessor = donutProcessor; + this.outputResult = outputResult; + } + + /** + * ドーナツの絵のカウント処理を実行するメソッド. + */ + public void execute() { + // 入力処理 + final GroundPictureData groundPictureData = inputGroundPicture.load(); + + // 処理(ドーナツの絵のカウント) + final int donutCount = donutProcessor.countDonuts(groundPictureData); + + // 出力処理 + outputResult.print(donutCount); + + } +} diff --git a/ntakanabe/src/b138/DonutCounterExecutorTest.java b/ntakanabe/src/b138/DonutCounterExecutorTest.java new file mode 100644 index 0000000..4a1fe8c --- /dev/null +++ b/ntakanabe/src/b138/DonutCounterExecutorTest.java @@ -0,0 +1,45 @@ +package paiza.test.b138; + +import static org.mockito.Mockito.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.junit.MockitoJUnitRunner; +import paiza.product.b138.DonutCounterExecutor; +import paiza.product.b138.DonutProcessor; +import paiza.product.b138.GroundPictureData; +import paiza.product.b138.InputGroundPicture; +import paiza.product.b138.OutputResult; + +@RunWith(MockitoJUnitRunner.class) +public class DonutCounterExecutorTest { + + @Test + public void executeメソッドで各コンポーネントが正しい順序で呼び出されることを検証() { + // Mockオブジェクトの作成 + final InputGroundPicture mockInput = mock(InputGroundPicture.class); + final DonutProcessor mockProcessor = mock(DonutProcessor.class); + final OutputResult mockOutput = mock(OutputResult.class); + + // GroundPictureDataのダミーオブジェクト + final GroundPictureData dummyData = new GroundPictureData(3, 3, new String[] { + "###", "#.#", "###" + }); + + // モックの振る舞いを定義 + when(mockInput.load()).thenReturn(dummyData); + when(mockProcessor.countDonuts(dummyData)).thenReturn(1); + + // テスト対象のインスタンスを生成 + final DonutCounterExecutor executor = new DonutCounterExecutor(mockInput, mockProcessor, mockOutput); + + // メソッド実行 + executor.execute(); + + // 呼び出し順序の検証 + final InOrder inOrder = inOrder(mockInput, mockProcessor, mockOutput); + inOrder.verify(mockInput).load(); + inOrder.verify(mockProcessor).countDonuts(dummyData); + inOrder.verify(mockOutput).print(1); + } +} diff --git a/ntakanabe/src/b138/DonutProcessor.java b/ntakanabe/src/b138/DonutProcessor.java new file mode 100644 index 0000000..790cff8 --- /dev/null +++ b/ntakanabe/src/b138/DonutProcessor.java @@ -0,0 +1,106 @@ +package paiza.product.b138; + +/** + * 「ドーナツの絵」の検出とカウントを担当するクラス. 地上絵を走査し、ドーナツの絵のパターンを識別する. + */ +public class DonutProcessor { + + // ドーナツの絵のパターンが3x3であることを示す定数 + private static final int DONUT_PATTERN_SIZE = 3; + // ドーナツの絵の黒い部分を表す定数 + private static final char BLACK = '#'; + // ドーナツの絵の白い部分を表す定数 + private static final char WHITE = '.'; + + // ドーナツの絵の黒い部分の相対座標を保持する配列 + // 中心 (1,1) は含まない + private static final int[][] DONUT_BLACK_OFFSETS = { + {0, 0}, {0, 1}, {0, 2}, + {1, 0}, {1, 2}, + {2, 0}, {2, 1}, {2, 2} + }; + + /** + * 地上絵の中に含まれる「ドーナツの絵」の数を数えるメソッド. + * + * @param groundPictureData 地上絵のデータを含むGroundPictureDataオブジェクト + * @return 見つかった「ドーナツの絵」の総数 + */ + public int countDonuts(final GroundPictureData groundPictureData) { + final int height = groundPictureData.getHeight(); + final int width = groundPictureData.getWidth(); + final String[] line = groundPictureData.getLine(); + + // 入力値が3x3より小さい場合はドーナツの絵はなし(0を返す) + if (height < DONUT_PATTERN_SIZE || width < DONUT_PATTERN_SIZE) { + return 0; + } + + int donutCount = 0; + + // 地上絵の各セルを3x3の模様の左上の角として走査 + for (int row = 0; row <= height - DONUT_PATTERN_SIZE; row++) { + for (int col = 0; col <= width - DONUT_PATTERN_SIZE; col++) { + // 現在の3x3の模様の文字を抽出 + final char[][] testPattern = extractPattern(line, row, col); + + // 抽出した3x3の模様が「ドーナツの絵」のパターンであるかチェック + if (isDonutPattern(testPattern)) { + donutCount++; + } + } + } + return donutCount; + } + + /** + * 指定された位置から3x3の模様の文字を抽出するメソッド. + * + * @param line 元の地上絵のグリッド + * @param startRow 判定対象の開始行インデックス + * @param startCol 判定対象の開始列インデックス + * @return 抽出された3x3の文字配列 + */ + private char[][] extractPattern(final String[] line, final int startRow, final int startCol) { + final char[][] testPattern = new char[DONUT_PATTERN_SIZE][DONUT_PATTERN_SIZE]; + for (int i = 0; i < DONUT_PATTERN_SIZE; i++) { + for (int j = 0; j < DONUT_PATTERN_SIZE; j++) { + testPattern[i][j] = line[startRow + i].charAt(startCol + j); + } + } + return testPattern; + } + + /** + * 指定された3x3の文字配列がドーナツの絵の模様であるか判定するメソッド. + * + * @param testPattern 判定する3x3の文字配列 + * @return 「ドーナツの絵」の模様であればtrue、そうでなければfalse + */ + private boolean isDonutPattern(final char[][] testPattern) { + // 中心が白(.)であること + final boolean isCenterWhite = (testPattern[1][1] == WHITE); + + // 周囲の8マスが黒(#)であること + final boolean isSurroundingBlack = checkSurroundingBlack(testPattern); + + return isCenterWhite && isSurroundingBlack; + } + + /** + * 指定された3x3の模様において、中心以外の8マスがすべて黒(#)であるかを確認するメソッド. + * + * @param testPattern 判定する3x3の文字配列 + * @return 周囲の8マスがすべて黒であればtrue、そうでなければfalse + */ + private boolean checkSurroundingBlack(final char[][] testPattern) { + for (int[] offset : DONUT_BLACK_OFFSETS) { + final int row = offset[0]; + final int col = offset[1]; + if (testPattern[row][col] != BLACK) { + return false; + } + } + return true; + } +} diff --git a/ntakanabe/src/b138/DonutProcessorTest.java b/ntakanabe/src/b138/DonutProcessorTest.java new file mode 100644 index 0000000..7d521b6 --- /dev/null +++ b/ntakanabe/src/b138/DonutProcessorTest.java @@ -0,0 +1,291 @@ +package paiza.test.b138; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.lang.reflect.Method; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; +import paiza.product.b138.DonutProcessor; +import paiza.product.b138.GroundPictureData; + +@RunWith(Enclosed.class) +public class DonutProcessorTest { + + public static class ドーナツの絵をカウントする処理テスト { + private DonutProcessor donutProcessor; + + @Before + public void setUp() { + donutProcessor = new DonutProcessor(); + } + + @Test + public void 地上絵にドーナツの絵が1つ含まれている場合1を返す() { + final String[] lines = { + "...", + "###", + "#.#", + "###" + }; + final GroundPictureData data = new GroundPictureData(4, 3, lines); + assertThat(donutProcessor.countDonuts(data), is(equalTo(1))); + } + + @Test + public void 地上絵にドーナツの絵が2つ含まれている場合2を返す() { + final String[] lines = { + "###.###", + "#.#.#.#", + "###.###" + }; + final GroundPictureData data = new GroundPictureData(3, 7, lines); + assertThat(donutProcessor.countDonuts(data), is(equalTo(2))); + } + + @Test + public void 地上絵にドーナツの絵が含まれていない場合0を返す() { + final String[] lines = { + "...", + ".#.", + "..." + }; + final GroundPictureData data = new GroundPictureData(3, 3, lines); + assertThat(donutProcessor.countDonuts(data), is(equalTo(0))); + } + + @Test + public void 地上絵のサイズがドーナツパターンより小さい場合にカウントが0になること() { + final String[] lines = { + "##", + "##" + }; + final GroundPictureData data = new GroundPictureData(2, 2, lines); + assertThat(donutProcessor.countDonuts(data), is(equalTo(0))); + } + + @Test + public void 地上絵のサイズが3_3より小さい場合0を返す() { + final String[] lines = { + "###", + "#.#" + }; + final GroundPictureData data = new GroundPictureData(2, 3, lines); + assertThat(donutProcessor.countDonuts(data), is(equalTo(0))); + } + } + + @RunWith(Theories.class) + public static class ドーナツの絵の模様か判断するテスト { + private DonutProcessor donutProcessor; + + @Before + public void setUp() { + donutProcessor = new DonutProcessor(); + } + + @DataPoints + public static TestPatternData[] isDonutPatternテストデータを提供() { + return new TestPatternData[] { + new TestPatternData(new char[][] { + { + '#', '#', '#' + }, { + '#', '.', '#' + }, { + '#', '#', '#' + } + }, true, "有効なドーナツパターン"), + new TestPatternData(new char[][] { + { + '#', '#', '#' + }, { + '#', '#', '#' + }, { + '#', '#', '#' + } + }, false, "中心が黒"), + new TestPatternData(new char[][] { + { + '#', '#', '#' + }, { + '#', '.', '.' + }, { + '#', '#', '#' + } + }, false, "周囲に白が含まれる"), + new TestPatternData(new char[][] { + { + '.', '.', '.' + }, { + '.', '.', '.' + }, { + '.', '.', '.' + } + }, false, "すべて白"), + new TestPatternData(new char[][] { + { + '#', '.', '#' + }, { + '.', '.', '.' + }, { + '#', '.', '#' + } + }, false, "混在パターン") + }; + } + + @Theory + public void isDonutPatternメソッドが正しくドーナツパターンを判定する(final TestPatternData data) throws Exception { + final Method method = DonutProcessor.class.getDeclaredMethod("isDonutPattern", char[][].class); + method.setAccessible(true); + + final boolean actual = (boolean) method.invoke(donutProcessor, (Object) data.testPattern); + assertThat(actual, is(equalTo(data.expected))); + } + + // テストデータを保持するためのヘルパークラス + static class TestPatternData { + final char[][] testPattern; + final boolean expected; + final String description; + + TestPatternData(final char[][] testPattern, final boolean expected, final String description) { + this.testPattern = testPattern; + this.expected = expected; + this.description = description; + } + + @Override + public String toString() { + // テスト結果で表示される名前を分かりやすくするため + return description; + } + } + } + + @RunWith(Theories.class) + public static class 周囲が黒マスかチェックするテスト { + private DonutProcessor donutProcessor; + + @Before + public void setUp() { + donutProcessor = new DonutProcessor(); + } + + @DataPoints + public static TestPatternData[] checkSurroundingBlackテストデータを提供() { + return new TestPatternData[] { + new TestPatternData(new char[][] { + { + '#', '#', '#' + }, { + '#', '.', '#' + }, { + '#', '#', '#' + } + }, true, "周囲が全て黒"), + new TestPatternData(new char[][] { + { + '#', '#', '#' + }, { + '#', '.', '.' + }, { + '#', '#', '#' + } + }, false, "周囲に白が1つ"), + new TestPatternData(new char[][] { + { + '.', '.', '.' + }, { + '.', '.', '.' + }, { + '.', '.', '.' + } + }, false, "周囲が全て白"), + new TestPatternData(new char[][] { + { + '#', '.', '#' + }, { + '.', '.', '#' + }, { + '#', '#', '.' + } + }, false, "周囲が混在") + }; + } + + @Theory + public void checkSurroundingBlackメソッドが正しく周囲の黒を判定する(final TestPatternData data) throws Exception { + final Method method = DonutProcessor.class + .getDeclaredMethod("checkSurroundingBlack", char[][].class); + method.setAccessible(true); + + final boolean actual = (boolean) method.invoke(donutProcessor, (Object) data.testPattern); + assertThat(actual, is(equalTo(data.expected))); + } + + static class TestPatternData { + final char[][] testPattern; + final boolean expected; + final String description; + + TestPatternData(final char[][] testPattern, final boolean expected, final String description) { + this.testPattern = testPattern; + this.expected = expected; + this.description = description; + } + + @Override + public String toString() { + return description; + } + } + } + + public static class パターン抽出テスト { + private DonutProcessor donutProcessor; + + @Before + public void setUp() { + donutProcessor = new DonutProcessor(); + } + + @Test + public void extractPatternメソッドが正しく3x3のパターンを抽出する() throws Exception { + final String[] line = { + "ABCDEF", + "GHIJKL", + "MNOPQR", + "STUVWX" + }; + final int startRow = 1; + final int startCol = 2; + + final Method method = DonutProcessor.class + .getDeclaredMethod("extractPattern", String[].class, int.class, int.class); + method.setAccessible(true); + + final char[][] expected = { + { + 'I', 'J', 'K' + }, + { + 'O', 'P', 'Q' + }, + { + 'U', 'V', 'W' + } + }; + final char[][] actual = (char[][]) method.invoke(donutProcessor, line, startRow, startCol); + + assertArrayEquals(expected[0], actual[0]); + assertArrayEquals(expected[1], actual[1]); + assertArrayEquals(expected[2], actual[2]); + } + } +} diff --git a/ntakanabe/src/b138/GroundPictureData.java b/ntakanabe/src/b138/GroundPictureData.java new file mode 100644 index 0000000..ca0a41d --- /dev/null +++ b/ntakanabe/src/b138/GroundPictureData.java @@ -0,0 +1,51 @@ +package paiza.product.b138; + +/** + * 地上絵のデータを保持するクラス. + */ +public class GroundPictureData { + + private final int height; + private final int width; + private final String[] line; + + /** + * 地上絵のデータのインスタンスを初期化するコンストラクタ. + * + * @param height 地上絵の縦方向のマス数 + * @param width 地上絵の横方向のマス数 + * @param line 地上絵の各行の模様を表す文字列の配列 + */ + public GroundPictureData(final int height, final int width, final String[] line) { + this.height = height; + this.width = width; + this.line = line; + } + + /** + * 地上絵の縦方向のマス数を取得. + * + * @return 地上絵の縦方向のマス数 + */ + public int getHeight() { + return height; + } + + /** + * 地上絵の横方向のマス数を取得. + * + * @return 地上絵の横方向のマス数 + */ + public int getWidth() { + return width; + } + + /** + * 地上絵の各行の文字列を取得. + * + * @return 地上絵の各行の文字列配列 + */ + public String[] getLine() { + return line; + } +} diff --git a/ntakanabe/src/b138/GroundPictureDataTest.java b/ntakanabe/src/b138/GroundPictureDataTest.java new file mode 100644 index 0000000..1dfd9a8 --- /dev/null +++ b/ntakanabe/src/b138/GroundPictureDataTest.java @@ -0,0 +1,24 @@ +package paiza.test.b138; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import org.junit.Test; +import paiza.product.b138.GroundPictureData; + +public class GroundPictureDataTest { + + @Test + public void コンストラクタとゲッターメソッドの設定値を正しく取得する() { + final int expectedHeight = 5; + final int expectedWidth = 10; + final String[] expectedLine = { + "...", "...", "...", "...", "..." + }; + + final GroundPictureData data = new GroundPictureData(expectedHeight, expectedWidth, expectedLine); + + assertThat(data.getHeight(), is(equalTo(expectedHeight))); + assertThat(data.getWidth(), is(equalTo(expectedWidth))); + assertArrayEquals(expectedLine, data.getLine()); + } +} diff --git a/ntakanabe/src/b138/InputGroundPicture.java b/ntakanabe/src/b138/InputGroundPicture.java new file mode 100644 index 0000000..6337279 --- /dev/null +++ b/ntakanabe/src/b138/InputGroundPicture.java @@ -0,0 +1,34 @@ +package paiza.product.b138; + +import java.util.Scanner; + +/** + * 入力処理を担当するクラス. + */ +public class InputGroundPicture { + + private final Scanner sc; + + public InputGroundPicture(final Scanner sc) { + this.sc = sc; + } + + /** + * 標準入力から地上絵の高さ、幅、および各行の文字列を読み込むメソッド. + * + * @return 読み込まれた地上絵のデータを含むGroundPictureDataオブジェクト + */ + public GroundPictureData load() { + final int height = sc.nextInt(); + final int width = sc.nextInt(); + sc.nextLine(); + + // 地上絵の各行を読み込む + final String[] line = new String[height]; + for (int i = 0; i < height; i++) { + line[i] = sc.nextLine(); + } + + return new GroundPictureData(height, width, line); + } +} diff --git a/ntakanabe/src/b138/InputGroundPictureTest.java b/ntakanabe/src/b138/InputGroundPictureTest.java new file mode 100644 index 0000000..f2d5feb --- /dev/null +++ b/ntakanabe/src/b138/InputGroundPictureTest.java @@ -0,0 +1,101 @@ +package paiza.test.b138; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.InputMismatchException; +import java.util.NoSuchElementException; +import java.util.Scanner; +import org.junit.Test; +import paiza.product.b138.GroundPictureData; +import paiza.product.b138.InputGroundPicture; + +public class InputGroundPictureTest { + + @Test + public void loadメソッドで標準入力から正しいデータを読み込む() { + final String input = "3 5\n" + + + ".....\n" + + + ".###.\n" + + + ".....\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + final Scanner sc = new Scanner(inputStream); + + final InputGroundPicture inputGroundPicture = new InputGroundPicture(sc); + final GroundPictureData data = inputGroundPicture.load(); + + assertThat(data, is(notNullValue())); + assertThat(data.getHeight(), is(equalTo(3))); + assertThat(data.getWidth(), is(equalTo(5))); + assertThat(data.getLine(), is(arrayContaining(".....", ".###.", "....."))); + + sc.close(); + } + + @Test + public void loadメソッドで空の入力に対して正しく動作する() { + final String input = "0 0\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + final Scanner sc = new Scanner(inputStream); + + final InputGroundPicture inputGroundPicture = new InputGroundPicture(sc); + final GroundPictureData data = inputGroundPicture.load(); + + assertThat(data, is(notNullValue())); + assertThat(data.getHeight(), is(equalTo(0))); + assertThat(data.getWidth(), is(equalTo(0))); + assertThat(data.getLine(), is(emptyArray())); + + sc.close(); + } + + @Test + public void loadメソッドでHとWの後の改行を消費することを確認() { + final String input = "1 1\n#\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + final Scanner sc = new Scanner(inputStream); + + final InputGroundPicture inputGroundPicture = new InputGroundPicture(sc); + inputGroundPicture.load(); + + // hasNextLine() が false であることを検証 + assertThat(sc.hasNextLine(), is(equalTo(false))); + + sc.close(); + } + + @Test(expected = InputMismatchException.class) + public void loadメソッドで数値以外の入力をした場合に例外をスローする() { + final String input = "A B\n"; + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + final Scanner sc = new Scanner(inputStream); + + final InputGroundPicture inputGroundPicture = new InputGroundPicture(sc); + inputGroundPicture.load(); + + sc.close(); + } + + @Test(expected = NoSuchElementException.class) + public void loadメソッドで行数が指定より少ない場合に例外をスローする() { + final String input = "3 3\n" + + + "###\n" + + + "#.#\n"; // 3行必要だが2行しかない + final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); + final Scanner sc = new Scanner(inputStream); + + final InputGroundPicture inputGroundPicture = new InputGroundPicture(sc); + inputGroundPicture.load(); + + sc.close(); + } +} diff --git a/ntakanabe/src/b138/OutputResult.java b/ntakanabe/src/b138/OutputResult.java new file mode 100644 index 0000000..d9f845a --- /dev/null +++ b/ntakanabe/src/b138/OutputResult.java @@ -0,0 +1,29 @@ +package paiza.product.b138; + +import java.io.PrintStream; + +/** + * 出力処理を担当するクラス. + */ +public class OutputResult { + + private final PrintStream printStream; + + /** + * OutputResultの新しいインスタンスを初期化するコンストラクタ. + * + * @param printStream 結果の出力に使用するPrintStreamオブジェクト + */ + public OutputResult(final PrintStream printStream) { + this.printStream = printStream; + } + + /** + * 計算された「ドーナツの絵」の数を出力するメソッド. + * + * @param count 出力するドーナツの絵の数 + */ + public void print(final int count) { + printStream.println(count); + } +} diff --git a/ntakanabe/src/b138/OutputResultTest.java b/ntakanabe/src/b138/OutputResultTest.java new file mode 100644 index 0000000..18b1ee0 --- /dev/null +++ b/ntakanabe/src/b138/OutputResultTest.java @@ -0,0 +1,61 @@ +package paiza.test.b138; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import paiza.product.b138.OutputResult; + +public class OutputResultTest { + + private ByteArrayOutputStream outputStreamCaptor; + private PrintStream originalOut; + + @Before + public void setUp() { + outputStreamCaptor = new ByteArrayOutputStream(); + originalOut = System.out; + System.setOut(new PrintStream(outputStreamCaptor)); + } + + @After + public void tearDown() { + System.setOut(originalOut); + } + + @Test + public void printメソッドで正しいカウント値を出力ストリームに書き込む() { + final OutputResult outputResult = new OutputResult(System.out); + final int testCount = 5; + + outputResult.print(testCount); + + assertThat(outputStreamCaptor.toString(), is(equalTo(String.valueOf(testCount) + System + .lineSeparator()))); + } + + @Test + public void printメソッドでゼロのカウント値を出力ストリームに書き込む() { + final OutputResult outputResult = new OutputResult(System.out); + final int testCount = 0; + + outputResult.print(testCount); + + assertThat(outputStreamCaptor.toString(), is(equalTo(String.valueOf(testCount) + System + .lineSeparator()))); + } + + @Test + public void printメソッドで負のカウント値を出力ストリームに書き込む() { + final OutputResult outputResult = new OutputResult(System.out); + final int testCount = -1; + + outputResult.print(testCount); + + assertThat(outputStreamCaptor.toString(), is(equalTo(String.valueOf(testCount) + System + .lineSeparator()))); + } +} -- GitLab From 2fa6c2b67b59b4cae0f5007dedc8599f9a58404f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=99=E9=8D=8B=20=E5=B0=9A=E8=BC=9D?= Date: Fri, 25 Jul 2025 13:51:06 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E9=96=A2=E6=95=B0=E5=90=8D=E3=80=81?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=83=87=E3=83=BC=E3=82=BF=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ntakanabe/src/b138/B138.java | 2 +- ntakanabe/src/b138/B138Test.java | 39 +++++++------------ ...CounterExecutor.java => DonutCounter.java} | 4 +- ...xecutorTest.java => DonutCounterTest.java} | 6 +-- ntakanabe/src/b138/DonutProcessor.java | 4 +- .../src/b138/InputGroundPictureTest.java | 15 +++---- 6 files changed, 26 insertions(+), 44 deletions(-) rename ntakanabe/src/b138/{DonutCounterExecutor.java => DonutCounter.java} (91%) rename ntakanabe/src/b138/{DonutCounterExecutorTest.java => DonutCounterTest.java} (88%) diff --git a/ntakanabe/src/b138/B138.java b/ntakanabe/src/b138/B138.java index 8cc6315..0e4ebdc 100644 --- a/ntakanabe/src/b138/B138.java +++ b/ntakanabe/src/b138/B138.java @@ -16,7 +16,7 @@ public class B138 { */ public static void main(final String[] args) { try (Scanner sc = new Scanner(System.in)) { - final DonutCounterExecutor sut = new DonutCounterExecutor(new InputGroundPicture( + final DonutCounter sut = new DonutCounter(new InputGroundPicture( sc), new DonutProcessor(), new OutputResult(System.out)); diff --git a/ntakanabe/src/b138/B138Test.java b/ntakanabe/src/b138/B138Test.java index a1973f8..05d03c2 100644 --- a/ntakanabe/src/b138/B138Test.java +++ b/ntakanabe/src/b138/B138Test.java @@ -41,12 +41,9 @@ public class B138Test { public void 地上絵にドーナツの絵が1つ含まれている場合1を返す() { // テストデータ final String input = "3 3\n" - + - "###\n" - + - "#.#\n" - + - "###\n"; + + "###\n" + + "#.#\n" + + "###\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); System.setIn(inputStream); @@ -59,12 +56,9 @@ public class B138Test { @Test public void 地上絵にドーナツの絵が2つ含まれている場合2を返す() { final String input = "3 7\n" - + - "###.###\n" - + - "#.#.#.#\n" - + - "###.###\n"; + + "###.###\n" + + "#.#.#.#\n" + + "###.###\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); System.setIn(inputStream); @@ -77,12 +71,9 @@ public class B138Test { @Test public void 地上絵にドーナツの絵が含まれていない場合0を返す() { final String input = "3 3\n" - + - "...\n" - + - ".#.\n" - + - "...\n"; + + "...\n" + + ".#.\n" + + "...\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); System.setIn(inputStream); @@ -95,10 +86,8 @@ public class B138Test { @Test public void 地上絵のサイズが3_3より小さい場合0を返す() { final String input = "2 2\n" - + - "##\n" - + - "##\n"; + + "##\n" + + "##\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); System.setIn(inputStream); @@ -112,10 +101,8 @@ public class B138Test { public void 地上絵の縦と横方向のマス数の入力後に改行していない場合例外がスローされる() { // sc.nextLine()が呼び出されないケース final String input = "3 3###\n" - + - "#.#\n" - + - "###\n"; + + "#.#\n" + + "###\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); System.setIn(inputStream); diff --git a/ntakanabe/src/b138/DonutCounterExecutor.java b/ntakanabe/src/b138/DonutCounter.java similarity index 91% rename from ntakanabe/src/b138/DonutCounterExecutor.java rename to ntakanabe/src/b138/DonutCounter.java index 38aa363..48b2d66 100644 --- a/ntakanabe/src/b138/DonutCounterExecutor.java +++ b/ntakanabe/src/b138/DonutCounter.java @@ -3,7 +3,7 @@ package paiza.product.b138; /** * ドーナツの絵のカウント処理を実行するクラス. */ -public class DonutCounterExecutor { +public class DonutCounter { private final InputGroundPicture inputGroundPicture; private final DonutProcessor donutProcessor; @@ -16,7 +16,7 @@ public class DonutCounterExecutor { * @param donutProcessor ドーナツカウント処理クラス * @param outputResult 出力処理クラス */ - public DonutCounterExecutor(final InputGroundPicture inputGroundPicture, + public DonutCounter(final InputGroundPicture inputGroundPicture, final DonutProcessor donutProcessor, final OutputResult outputResult) { this.inputGroundPicture = inputGroundPicture; diff --git a/ntakanabe/src/b138/DonutCounterExecutorTest.java b/ntakanabe/src/b138/DonutCounterTest.java similarity index 88% rename from ntakanabe/src/b138/DonutCounterExecutorTest.java rename to ntakanabe/src/b138/DonutCounterTest.java index 4a1fe8c..14dbcf1 100644 --- a/ntakanabe/src/b138/DonutCounterExecutorTest.java +++ b/ntakanabe/src/b138/DonutCounterTest.java @@ -5,14 +5,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.junit.MockitoJUnitRunner; -import paiza.product.b138.DonutCounterExecutor; +import paiza.product.b138.DonutCounter; import paiza.product.b138.DonutProcessor; import paiza.product.b138.GroundPictureData; import paiza.product.b138.InputGroundPicture; import paiza.product.b138.OutputResult; @RunWith(MockitoJUnitRunner.class) -public class DonutCounterExecutorTest { +public class DonutCounterTest { @Test public void executeメソッドで各コンポーネントが正しい順序で呼び出されることを検証() { @@ -31,7 +31,7 @@ public class DonutCounterExecutorTest { when(mockProcessor.countDonuts(dummyData)).thenReturn(1); // テスト対象のインスタンスを生成 - final DonutCounterExecutor executor = new DonutCounterExecutor(mockInput, mockProcessor, mockOutput); + final DonutCounter executor = new DonutCounter(mockInput, mockProcessor, mockOutput); // メソッド実行 executor.execute(); diff --git a/ntakanabe/src/b138/DonutProcessor.java b/ntakanabe/src/b138/DonutProcessor.java index 790cff8..edf65ac 100644 --- a/ntakanabe/src/b138/DonutProcessor.java +++ b/ntakanabe/src/b138/DonutProcessor.java @@ -82,7 +82,7 @@ public class DonutProcessor { final boolean isCenterWhite = (testPattern[1][1] == WHITE); // 周囲の8マスが黒(#)であること - final boolean isSurroundingBlack = checkSurroundingBlack(testPattern); + final boolean isSurroundingBlack = isSurroundingBlack(testPattern); return isCenterWhite && isSurroundingBlack; } @@ -93,7 +93,7 @@ public class DonutProcessor { * @param testPattern 判定する3x3の文字配列 * @return 周囲の8マスがすべて黒であればtrue、そうでなければfalse */ - private boolean checkSurroundingBlack(final char[][] testPattern) { + private boolean isSurroundingBlack(final char[][] testPattern) { for (int[] offset : DONUT_BLACK_OFFSETS) { final int row = offset[0]; final int col = offset[1]; diff --git a/ntakanabe/src/b138/InputGroundPictureTest.java b/ntakanabe/src/b138/InputGroundPictureTest.java index f2d5feb..41209fb 100644 --- a/ntakanabe/src/b138/InputGroundPictureTest.java +++ b/ntakanabe/src/b138/InputGroundPictureTest.java @@ -19,12 +19,9 @@ public class InputGroundPictureTest { @Test public void loadメソッドで標準入力から正しいデータを読み込む() { final String input = "3 5\n" - + - ".....\n" - + - ".###.\n" - + - ".....\n"; + + ".....\n" + + ".###.\n" + + ".....\n"; final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); final Scanner sc = new Scanner(inputStream); @@ -86,10 +83,8 @@ public class InputGroundPictureTest { @Test(expected = NoSuchElementException.class) public void loadメソッドで行数が指定より少ない場合に例外をスローする() { final String input = "3 3\n" - + - "###\n" - + - "#.#\n"; // 3行必要だが2行しかない + + "###\n" + + "#.#\n"; // 3行必要だが2行しかない final InputStream inputStream = new ByteArrayInputStream(input.getBytes()); final Scanner sc = new Scanner(inputStream); -- GitLab