<commit_before>module XO

  class Grid

    ROWS = 3
    COLS = 3

    def self.contains?(r, c)
      r.between?(1, ROWS) && c.between?(1, COLS)
    end

    def initialize
      @grid = Array.new(ROWS * COLS, :e)
    end

    def initialize_copy(orig)
      @grid = orig.instance_variable_get(:@grid).dup
    end

    def []=(r, c, val)
      if self.class.contains?(r, c)
        grid[idx(r, c)] = val
      else
        raise IndexError, "position (#{r}, #{c}) is off the grid"
      end
    end

    def [](r, c)
      if self.class.contains?(r, c)
        grid[idx(r, c)]
      else
        raise IndexError, "position (#{r}, #{c}) is off the grid"
      end
    end

    def empty?
      grid.all? { |val| !XO.is_token?(val) }
    end

    def full?
      grid.all? { |val| XO.is_token?(val) }
    end

    def free?(r, c)
      !XO.is_token?(self[r, c])
    end

    def clear
      grid.fill(:e)
    end

    def each
      (1..ROWS).each do |r|
        (1..COLS).each do |c|
          yield(r, c, self[r, c])
        end
      end

      self
    end

    def each_free
      self.each { |r, c, _| yield(r, c) if free?(r, c) }
    end

    def ==(other)
      return false unless other.instance_of?(self.class)
      grid == other.instance_variable_get(:@grid)
    end
    alias_method :eql?, :==

    def hash
      grid.hash
    end

    private

      attr_reader :grid

      # Computes the 0-based index of position (r, c) on a 3x3 grid.
      #
      #    c  1   2   3
      #  r
      #  1    0 | 1 | 2
      #      ---+---+---
      #  2    3 | 4 | 5
      #      ---+---+---
      #  3    6 | 7 | 8
      #
      # For e.g. idx(2, 3) is 5.
      def idx(r, c)
        COLS * (r - 1) + (c - 1)
      end
  end
end<commit_msg>Add a to_s method for displaying a friendly version of a grid<commit_after>module XO

  class Grid

    ROWS = 3
    COLS = 3

    def self.contains?(r, c)
      r.between?(1, ROWS) && c.between?(1, COLS)
    end

    def initialize
      @grid = Array.new(ROWS * COLS, :e)
    end

    def initialize_copy(orig)
      @grid = orig.instance_variable_get(:@grid).dup
    end

    def []=(r, c, val)
      if self.class.contains?(r, c)
        grid[idx(r, c)] = val
      else
        raise IndexError, "position (#{r}, #{c}) is off the grid"
      end
    end

    def [](r, c)
      if self.class.contains?(r, c)
        grid[idx(r, c)]
      else
        raise IndexError, "position (#{r}, #{c}) is off the grid"
      end
    end

    def empty?
      grid.all? { |val| !XO.is_token?(val) }
    end

    def full?
      grid.all? { |val| XO.is_token?(val) }
    end

    def free?(r, c)
      !XO.is_token?(self[r, c])
    end

    def clear
      grid.fill(:e)
    end

    def each
      (1..ROWS).each do |r|
        (1..COLS).each do |c|
          yield(r, c, self[r, c])
        end
      end

      self
    end

    def each_free
      self.each { |r, c, _| yield(r, c) if free?(r, c) }
    end

    def ==(other)
      return false unless other.instance_of?(self.class)
      grid == other.instance_variable_get(:@grid)
    end
    alias_method :eql?, :==

    def hash
      grid.hash
    end

    def to_s
      [" #{t grid[0]} | #{t grid[1]} | #{t grid[2]} ",
       "---+---+---",
       " #{t grid[3]} | #{t grid[4]} | #{t grid[5]} ",
       "---+---+---",
       " #{t grid[6]} | #{t grid[7]} | #{t grid[8]} "].join("\n")
    end

    private

      attr_reader :grid

      # Computes the 0-based index of position (r, c) on a 3x3 grid.
      #
      #    c  1   2   3
      #  r
      #  1    0 | 1 | 2
      #      ---+---+---
      #  2    3 | 4 | 5
      #      ---+---+---
      #  3    6 | 7 | 8
      #
      # For e.g. idx(2, 3) is 5.
      def idx(r, c)
        COLS * (r - 1) + (c - 1)
      end

      def t(val)
        XO.is_token?(val) ? val : ' '
      end
  end
end
{'fraction_non_alphanumeric': 0.16171875, 'fraction_numerical': 0.017447916666666667, 'mean_word_length': 1.4387301587301586, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '2828254', 'n_tokens_mistral': 1521, 'n_tokens_neox': 1474, 'n_words': 403}