[JAVA] We have refactored the "Bingo Judgment Program"

After seeing @ johejo's post "Bingo Judgment Program", I thought it was an interesting story, but it was all a static method procedural program. I was disappointed that it was. Since it's Java, let's refactor it in an object-oriented manner. Of course, this is not the final form. I think you can do better refactoring, so please try it out by trial and error.

3rd refactoring

I received a comment from @ k73i55no5 again and refactored only the Bingo class.

Class structure overview

Class diagram

image.png

Code (Bingo class only)

public class Bingo {
    public static void main(String[] args) throws Exception {
        Bingo bingo = new Bingo(loadCardNumbers("board.txt"));
        bingo.select(loadSelectionNumbers("selected.txt"));
        bingo.show();
    }

    public static int[][] loadCardNumbers(String filename) throws IOException {
        return Files.readAllLines(Paths.get(filename))
                    .stream()
                    .map(line -> Arrays.stream(line.split(" "))
                                       .mapToInt(Integer::parseInt)
                                       .toArray())
                    .toArray(int[][]::new);
    }

    public static int[] loadSelectionNumbers(String filename) throws IOException {
        return Files.readAllLines(Paths.get(filename))
                    .stream()
                    .mapToInt(Integer::parseInt)
                    .toArray();
    }

    private final BingoCard card;

    public Bingo(int[][] numbers) {
        this(new BingoCard(numbers));
    }

    Bingo(BingoCard card) {
        this.card = card;
    }

    public void select(int[] numbers) {
        Arrays.stream(numbers).forEach(card::select);
    }

    public void show() {
        System.out.println("BINGO:" + card.countBingo());
        System.out.println("REACH:" + card.countReach());
    }
}

Second refactoring

Based on the comments made by @ k73i55no5, we carried out the second refactoring. The cell of the bingo card is fixed at 5x5, and the data size consistency check is omitted. If you have any further improvements, please comment.

code

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Stream;

public class Bingo {
    public static void main(String[] args) throws Exception {
        //Read the number data of the bingo card and create a bingo card
        int[][] numbers = Files.readAllLines(Paths.get("board.txt"))
                               .stream()
                               .map(line -> Arrays.stream(line.split(" "))
                                                  .mapToInt(Integer::parseInt)
                                                  .toArray())
                               .toArray(int[][]::new);
        BingoCard card = new BingoCard(numbers);
        //Read the number string selected by the bingo tournament organizer and reflect it on the bingo card
        Files.readAllLines(Paths.get("selected.txt"))
             .stream()
             .map(Integer::parseInt)
             .forEach(card::select);
        //Display the status of bingo cards
        System.out.println("BINGO:" + card.countBingo());
        System.out.println("REACH:" + card.countReach());
    }
}

class BingoCard {
    private final SelectableNumbers numbers;
    private final BingoLines lines;

    public BingoCard(int[][] numbers) {
        this.numbers = new SelectableNumbers(numbers);
        this.lines = new BingoLines(this.numbers);
    }

    public void select(int number) {
        numbers.select(number);
    }

    public long countReach() {
        return lines.countReach();
    }

    public long countBingo() {
        return lines.countBingo();
    }
}

class SelectableNumber {
    public final int number;
    private boolean selected = false;

    public SelectableNumber(int number) {
        this.number = number;
    }

    public void select() {
        selected = true;
    }

    public boolean isSelected() {
        return selected;
    }
}

class SelectableNumbers {
    private final SelectableNumber[] numbers;

    private static SelectableNumber[] flattenNumbers(int[][] numbers) {
        return Stream.of(numbers)
                     .flatMapToInt(Arrays::stream)
                     .mapToObj(SelectableNumber::new)
                     .toArray(SelectableNumber[]::new);
    }
    
    public SelectableNumbers(int[][] numbers) {
        this.numbers = flattenNumbers(numbers);
    }
    
    public SelectableNumber getAt(int index) {
        return numbers[index];
    }

    public void select(int number) {
        Stream.of(numbers)
              .filter(n -> n.number == number)
              .forEach(n -> n.select());
    }
}

class BingoLine {
    private final SelectableNumber[] numbers;

    public BingoLine(SelectableNumber[] numbers) {
        this.numbers = numbers;
    }

    private long countSelectedNumbers() {
        return Stream.of(numbers).filter(number -> number.isSelected()).count();
    }

    public boolean isReach() {
        return countSelectedNumbers() == numbers.length - 1;
    }

    public boolean isBingo() {
        return countSelectedNumbers() == numbers.length;
    }
}

class BingoLines {
    //Line index number array, index number is 0 to 24 fixed
    private static final int[][] LINES = new int[][] {
        //Horizontal line Vertical line
        { 0,  1,  2,  3,  4}, {0, 5, 10, 15, 20},
        { 5,  6,  7,  8,  9}, {1, 6, 11, 16, 21},
        {10, 11, 12, 13, 14}, {2, 7, 12, 17, 22},
        {15, 16, 17, 18, 19}, {3, 8, 13, 18, 23},
        {20, 21, 21, 23, 24}, {4, 9, 14, 19, 24},
        //Diagonal line from top left to bottom left, top right to bottom left
        { 0,  6, 12, 18, 19}, {4, 8, 12, 16, 20},
    };
    
    private static BingoLine[] makeBingoLines(SelectableNumbers numbers) {
        return Stream.of(LINES)
                     .map(line -> Arrays.stream(line)
                                        .mapToObj(numbers::getAt)
                                        .toArray(SelectableNumber[]::new))
                     .map(BingoLine::new)
                     .toArray(BingoLine[]::new);
    }

    private final BingoLine[] lines;

    public BingoLines(SelectableNumbers numbers) {
        lines = makeBingoLines(numbers);
    }
    
    public long countReach() {
        return Stream.of(lines).filter(line -> line.isReach()).count();        
    }
    
    public long countBingo() {
        return Stream.of(lines).filter(line -> line.isBingo()).count();        
    }
}

1st refactoring

Random numbers in 5 rows and 5 columns are written on the bingo card. Strictly speaking, the range of numbers is decided for each column, but I don't care about that. At the bingo tournament, each person is dealt a paper card with a different number written on it, the organizer randomly selects a ball with the number written on it, and if that number is in the card, that number part Fold it to the selected state. Cards have rows, columns, and diagonal rows, with one row containing five numbers. If all the numbers in one line are selected, it will become "Bingo" and you will receive a prize. The state of becoming a bingo when one more is selected is called "reach".

The words "card", "number", and "line" have come up, so let's make them into the Card, Number, and Line classes. When the bingo tournament organizer chooses a number, he usually searches for the number in his bingo card and folds the paper to select it, but in the object-oriented world, the class can be anthropomorphized and used. I will. People: Hey Card, do you have this number? If there is, please choose. Card-kun: Oh, there was. Numbers, you were chosen. Number-kun: Oh, did you choose me? Tell me what you belong to. Nami-kun: Oh, the number chosen has increased by one. Card-kun, the number of numbers selected for me is now four. Card-kun: Oh, did you have all four, do you have a reach? One more time soon.

I tried to program such a situation.

code

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;

//Bingo card
class Card {

    //Numbers on bingo cards
    class Number {

        final int value;
        private boolean isSelected = false;
        private final List<Line> lines = new ArrayList<>();

        Number(int value) {
            this.value = value;
        }

        //Belongs to vertical, horizontal, diagonal rows
        void belong(Line line) {
            lines.add(line);
        }

        //The numbers were chosen
        void selected() {
            if (!isSelected) {
                isSelected = true;
                //Belong and inform
                lines.stream().forEach(line -> line.selected());
            }
        }
    }

    //Arrangement on bingo cards (vertical, horizontal, diagonal)
    class Line {

        private int selectedCount = 0;

        //The numbers in the line were chosen
        void selected() {
            selectedCount++;
            //Update card status
            judge(this);
        }
    }

    final int size;
    int bingo = 0, reach = 0;
    private final HashMap<Integer, Number> myNumbers = new HashMap<>();

    //Bingo card generation
    Card(int[][] numbers) {
        //Make vertical, horizontal and diagonal rows
        size = numbers.length;
        Line[] rowLines = new Line[size];   //Side by side
        Line[] colLines = new Line[size];   //Vertical arrangement
        Line backslashLine = new Line();    //Diagonal arrangement from top left to bottom right
        Line slashLine = new Line();        //Diagonal arrangement from top right to bottom left
        for (int i = 0; i < size; i++) {    //Initialize because the contents of the array are empty
            rowLines[i] = new Line();
            colLines[i] = new Line();
        }
        //Make and belong to each number
        for (int row = 0; row < size; row++) {
            for (int col = 0; col < size; col++) {
                Number number = new Number(numbers[row][col]);
                number.belong(rowLines[row]);
                number.belong(colLines[col]);
                if (row == col) {
                    number.belong(backslashLine);
                }
                if (row == size - col - 1) {
                    number.belong(slashLine);
                }
                myNumbers.put(number.value, number);
            }
        }
    }

    //If there is a number, make it the selected state
    void selected(int number) {
        Number myNumber = myNumbers.get(number);
        if (myNumber != null) {
            myNumber.selected();
        }
    }

    //Update the number of reach and bingo according to the number of selected numbers in the line
    void judge(Line line) {
        if (line.selectedCount == size - 1) {
            reach++;
        } else if (line.selectedCount == size) {
            reach--;    //Since it is counted at the time of reach, reduce it
            bingo++;
        }
    }
}

//Bingo tournament
public class Bingo {

    public static void main(String[] args) throws Exception {
        //Read the number data of the bingo card and create a bingo card
        List<String> board = Files.readAllLines(Paths.get("board.txt"));
        int size = board.size();
        int[][] numbers = new int[size][size];
        for (int row = 0; row < size; row++) {
            String[] values = board.get(row).split(" ");
            for (int col = 0; col < size; col++) {
                numbers[row][col] = Integer.parseInt(values[col]);
            }
        }
        Card card = new Card(numbers);
        //Read the number string selected by the bingo tournament organizer and reflect it on the bingo card
        Stream<String> selected = Files.lines(Paths.get("selected.txt"));
        selected.forEach(number -> card.selected(Integer.parseInt(number)));
        //Display the status of bingo cards
        System.out.println("BINGO:" + card.bingo + "\nREACH:" + card.reach);
    }
}

Input file

biard.txt


1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

selected.txt


1
7
13
19
2
3
4

Recommended Posts

We have refactored the "Bingo Judgment Program"
Bingo judgment program
We have extracted the best plastics of Dockerfile!
Judgment of the calendar