<commit_before>package vrampal.connectfour.core.impl;

import java.io.Serializable;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import vrampal.connectfour.core.Board;
import vrampal.connectfour.core.ConnectFourException;
import vrampal.connectfour.core.Player;
import vrampal.connectfour.core.data.PlayerData;

/**
 * Empty cell use null internally but it use EMPTY_PLAYER externally.
 */
@Slf4j
@EqualsAndHashCode(of = { "content" })
class BoardImpl implements Board, Serializable {

  private static final long serialVersionUID = -5581793932297360395L;

  static final Player EMPTY_PLAYER = new PlayerData("Empty", ' ');

  private static final int DEFAULT_WIDTH = 7;

  private static final int DEFAULT_HEIGHT = 6;

  private static final int LENGTH_TO_WIN = 4;

  private final Player[][] content;

  @Setter(AccessLevel.PACKAGE)
  private GameEndListener endGameListener;

  /**
   * Create a board with default size (7x6).
   */
  BoardImpl() {
    this(DEFAULT_WIDTH, DEFAULT_HEIGHT);
  }

  /**
   * Create a board with custom size.
   */
  BoardImpl(int width, int height) {
    if (width <= 0) {
      throw new ConnectFourException("Invalid width: " + width);
    }
    if (height <= 0) {
      throw new ConnectFourException("Invalid height: " + height);
    }

    content = new Player[width][];
    for (int colIdx = 0; colIdx < width; colIdx++) {
      content[colIdx] = new Player[height];
    }

    if (log.isInfoEnabled()) {
      log.info("Creating new board " + width + 'x' + height);
    }
  }

  // ----- Base methods for content manipulation -----

  @Override
  public int getWidth() {
    return content.length;
  }

  @Override
  public int getHeight() {
    return content[0].length;
  }

  private Player getCellFast(int colIdx, int rowIdx) {
    return content[colIdx][rowIdx];
  }

  private void setCellFast(int colIdx, int rowIdx, Player player) {
    content[colIdx][rowIdx] = player;
  }

  private boolean isColumnFullFast(Player[] column) {
    return column[column.length - 1] != null;
  }

  // ----- All the following methods rely on getWidth(), getHeight(), getCellFast() and setCellFast() -----

  // ----- Input validation methods -----

  private void checkColIdx(int colIdx) {
    if ((colIdx < 0) || (colIdx >= getWidth())) {
      String message = "Invalid column id: " + colIdx;
      log.error(message);
      throw new ConnectFourException(message);
    }
  }

  private void checkRowIdx(int rowIdx) {
    if ((rowIdx < 0) || (rowIdx >= getHeight())) {
      String message = "Invalid row id: " + rowIdx;
      log.error(message);
      throw new ConnectFourException(message);
    }
  }

  // ----- The following methods rely on checkColIdx() and checkRowIdx() -----

  @Override
  public Player getCell(int colIdx, int rowIdx) {
    checkColIdx(colIdx);
    checkRowIdx(rowIdx);
    Player player = getCellFast(colIdx, rowIdx);
    if (player == null) {
      player = EMPTY_PLAYER;
    }
    return player;
  }

  @Override
  public boolean isColumnFull(int colIdx) {
    checkColIdx(colIdx);
    Player[] column = content[colIdx];
    return isColumnFullFast(column);
  }

  @Override
  public boolean isFull() {
    for (Player[] column : content) {
      if (!isColumnFullFast(column)) {
        return false;
      }
    }
    return true;
  }

  // ----- Dropping and victory mechanics -----

  @Override
  public Player getEmptyPlayer() {
    return EMPTY_PLAYER;
  }

  /**
   * Player entry point, drop a disc into a given column.<commit_msg>Board based on a single dimension array<commit_after>package vrampal.connectfour.core.impl;

import java.io.Serializable;

import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import vrampal.connectfour.core.Board;
import vrampal.connectfour.core.ConnectFourException;
import vrampal.connectfour.core.Player;
import vrampal.connectfour.core.data.PlayerData;

/**
 * Empty cell use null internally but it use EMPTY_PLAYER externally.
 */
@Slf4j
@EqualsAndHashCode(of = { "content" })
class BoardImpl implements Board, Serializable {

  private static final long serialVersionUID = -5581793932297360395L;

  static final Player EMPTY_PLAYER = new PlayerData("Empty", ' ');

  private static final int DEFAULT_WIDTH = 7;

  private static final int DEFAULT_HEIGHT = 6;

  private static final int LENGTH_TO_WIN = 4;

  @Getter
  private final int width;

  @Getter
  private final int height;

  private final Player[] content;

  @Setter(AccessLevel.PACKAGE)
  private GameEndListener endGameListener;

  /**
   * Create a board with default size (7x6).
   */
  BoardImpl() {
    this(DEFAULT_WIDTH, DEFAULT_HEIGHT);
  }

  /**
   * Create a board with custom size.
   */
  BoardImpl(int width, int height) {
    if (width <= 0) {
      throw new ConnectFourException("Invalid width: " + width);
    }
    if (height <= 0) {
      throw new ConnectFourException("Invalid height: " + height);
    }

    this.width = width;
    this.height = height;
    content = new Player[width * height];

    if (log.isInfoEnabled()) {
      log.info("Creating new board " + width + 'x' + height);
    }
  }

  // ----- Base methods for content manipulation -----

  private Player getCellFast(int colIdx, int rowIdx) {
    return content[(colIdx * height) + rowIdx];
  }

  private void setCellFast(int colIdx, int rowIdx, Player player) {
    content[(colIdx * height) + rowIdx] = player;
  }

  private boolean isColumnFullFast(int colIdx) {
    return content[((colIdx + 1) * height) - 1] != null;
  }

  // ----- All the following methods rely on getWidth(), getHeight(), getCellFast() and setCellFast() -----

  // ----- Input validation methods -----

  private void checkColIdx(int colIdx) {
    if ((colIdx < 0) || (colIdx >= getWidth())) {
      String message = "Invalid column id: " + colIdx;
      log.error(message);
      throw new ConnectFourException(message);
    }
  }

  private void checkRowIdx(int rowIdx) {
    if ((rowIdx < 0) || (rowIdx >= getHeight())) {
      String message = "Invalid row id: " + rowIdx;
      log.error(message);
      throw new ConnectFourException(message);
    }
  }

  // ----- The following methods rely on checkColIdx() and checkRowIdx() -----

  @Override
  public Player getCell(int colIdx, int rowIdx) {
    checkColIdx(colIdx);
    checkRowIdx(rowIdx);
    Player player = getCellFast(colIdx, rowIdx);
    if (player == null) {
      player = EMPTY_PLAYER;
    }
    return player;
  }

  @Override
  public boolean isColumnFull(int colIdx) {
    checkColIdx(colIdx);
    return isColumnFullFast(colIdx);
  }

  @Override
  public boolean isFull() {
    for (int colIdx = 0; colIdx < width; colIdx++) {
      if (!isColumnFullFast(colIdx)) {
        return false;
      }
    }
    return true;
  }

  // ----- Dropping and victory mechanics -----

  @Override
  public Player getEmptyPlayer() {
    return EMPTY_PLAYER;
  }

  /**
   * Player entry point, drop a disc into a given column.
{'fraction_non_alphanumeric': 0.12226766323508673, 'fraction_numerical': 0.009589620645889155, 'mean_word_length': 3.0711825487944893, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7486705', 'n_tokens_mistral': 2295, 'n_tokens_neox': 2182, 'n_words': 608}