<commit_before>#!/usr/bin/env ruby

require 'json'

def assert_eq(a, b)
  if a != b
    raise "#{a} vs #{b}"
  end
end

class Lcg
  def initialize(seed)
    @v = seed
  end

  def raw
    r = (@v >> 16) & 0x7fff
    @v = (@v * 1103515245 + 12345) & (2 ** 32 - 1)
    r
  end

  def pick(n)
    raw % n
  end
end

def decode_cmd(s)
  r = []
  s.each_char do |c|
    if [?p, ?', ?!, ?., ?0, ?3].include?(c)
      r << [:move, :W]
    elsif [?b, ?c, ?e, ?f, ?y, ?2].include?(c)
      r << [:move, :E]
    elsif [?a, ?g, ?h, ?i, ?j, ?4].include?(c)
      r << [:move, :SW]
    elsif [?l, ?m, ?n, ?o, ' ', ?5].include?(c)
      r << [:move, :SE]
    elsif [?d, ?q, ?r, ?v, ?z, ?1].include?(c)
      r << [:turn, :C]
    elsif [?k, ?s, ?t, ?u, ?w, ?x].include?(c)
      r << [:turn, :CC]
    else
      raise "Unknown: #{c}"
    end
  end
  r
end

class Unit
  attr_accessor :members, :pivot

  def initialize(u, base_x)
    @members = []
    u['members'].each do |pos|
      @members << [pos['x'] + base_x, pos['y']]
    end
    @pivot = [u['pivot']['x'] + base_x, u['pivot']['y']]
  end

  def moved_pos(x, y, dir)
    dx = dy = 0
    case dir
    when :E
      dx = 1
      dy = 0
    when :SE
      dx = y % 2
      dy = 1
    when :W
      dx = -1
      dy = 0
    when :SW
      dx = -1 + y % 2
      dy = 1
    else
      raise "#{arg}"
    end
    [x + dx, y + dy]
  end

  def rotated_pos(x, y, dir)
    puts "rotated_pos (#{x}, #{y}), (#{@pivot[0]}, #{@pivot[1]})"
    xdist = Math.sqrt(3)/2
    ydist = 3.0/4.0
    geox = xdist * x + (1 + (y % 2)) * xdist / 2
    geoy = 0.5 + ydist * y
    geopx = xdist * @pivot[0] + (1 + (@pivot[1] % 2)) * xdist / 2
    geopy = 0.5 + ydist * @pivot[1]

    dx = geox - geopx
    dy = geoy - geopy
    case dir
    when :C
      theta = Math::PI / 3
    when :CC
      theta = - Math::PI / 3
    else
      raise "#{arg}"
    end

    geonx = dx * Math.cos(theta) - dy * Math.sin(theta) + geopx
    geony = dx * Math.sin(theta) + dy * Math.cos(theta) + geopy

    puts "rotated_pos geo (#{geox}, #{geoy}), (#{geopx}, #{geopy}), (#{geonx}, #{geony})"
    ny = ((geony - 0.5) / ydist).round
    nx = ((geonx - (1 + (ny % 2)) * xdist / 2) / xdist).round
    puts "rotated_pos after (#{nx}, #{ny})"
    [nx, ny]
  end

  def can_move(dir, board)
    @members.each do |x, y|
      nx, ny = moved_pos(x, y, dir)
      if (nx < 0 || ny < 0 || ny >= board.size || nx >= board[0].size ||
          board[ny][nx])
        return false
      end
    end
    return true
  end

  def move(dir, board)
    if can_move(dir, board)
      @members.each do |pos|
        x, y = pos
        nx, ny = moved_pos(x, y, dir)
        pos[0] = nx
        pos[1] = ny
      end
      return true
    else
      return false
    end
  end

  def can_turn(dir, board)
    @members.each do |x, y|
      nx, ny = rotated_pos(x, y, dir)
      if (nx < 0 || ny < 0 || ny >= board.size || nx >= board[0].size ||
          board[ny][nx])
        return false
      end
    end
    return true<commit_msg>Fixed to update pivot position on move.<commit_after>#!/usr/bin/env ruby

require 'json'

def assert_eq(a, b)
  if a != b
    raise "#{a} vs #{b}"
  end
end

class Lcg
  def initialize(seed)
    @v = seed
  end

  def raw
    r = (@v >> 16) & 0x7fff
    @v = (@v * 1103515245 + 12345) & (2 ** 32 - 1)
    r
  end

  def pick(n)
    raw % n
  end
end

def decode_cmd(s)
  r = []
  s.each_char do |c|
    if [?p, ?', ?!, ?., ?0, ?3].include?(c)
      r << [:move, :W]
    elsif [?b, ?c, ?e, ?f, ?y, ?2].include?(c)
      r << [:move, :E]
    elsif [?a, ?g, ?h, ?i, ?j, ?4].include?(c)
      r << [:move, :SW]
    elsif [?l, ?m, ?n, ?o, ' ', ?5].include?(c)
      r << [:move, :SE]
    elsif [?d, ?q, ?r, ?v, ?z, ?1].include?(c)
      r << [:turn, :C]
    elsif [?k, ?s, ?t, ?u, ?w, ?x].include?(c)
      r << [:turn, :CC]
    else
      raise "Unknown: #{c}"
    end
  end
  r
end

class Unit
  attr_accessor :members, :pivot

  def initialize(u, base_x)
    @members = []
    u['members'].each do |pos|
      @members << [pos['x'] + base_x, pos['y']]
    end
    @pivot = [u['pivot']['x'] + base_x, u['pivot']['y']]
  end

  def moved_pos(x, y, dir)
    dx = dy = 0
    case dir
    when :E
      dx = 1
      dy = 0
    when :SE
      dx = y % 2
      dy = 1
    when :W
      dx = -1
      dy = 0
    when :SW
      dx = -1 + y % 2
      dy = 1
    else
      raise "#{arg}"
    end
    [x + dx, y + dy]
  end

  def rotated_pos(x, y, dir)
    puts "rotated_pos (#{x}, #{y}), (#{@pivot[0]}, #{@pivot[1]})"
    xdist = Math.sqrt(3)/2
    ydist = 3.0/4.0
    geox = xdist * x + (1 + (y % 2)) * xdist / 2
    geoy = 0.5 + ydist * y
    geopx = xdist * @pivot[0] + (1 + (@pivot[1] % 2)) * xdist / 2
    geopy = 0.5 + ydist * @pivot[1]

    dx = geox - geopx
    dy = geoy - geopy
    case dir
    when :C
      theta = Math::PI / 3
    when :CC
      theta = - Math::PI / 3
    else
      raise "#{arg}"
    end

    geonx = dx * Math.cos(theta) - dy * Math.sin(theta) + geopx
    geony = dx * Math.sin(theta) + dy * Math.cos(theta) + geopy

    puts "rotated_pos geo (#{geox}, #{geoy}), (#{geopx}, #{geopy}), (#{geonx}, #{geony})"
    ny = ((geony - 0.5) / ydist).round
    nx = ((geonx - (1 + (ny % 2)) * xdist / 2) / xdist).round
    puts "rotated_pos after (#{nx}, #{ny})"
    [nx, ny]
  end

  def can_move(dir, board)
    @members.each do |x, y|
      nx, ny = moved_pos(x, y, dir)
      if (nx < 0 || ny < 0 || ny >= board.size || nx >= board[0].size ||
          board[ny][nx])
        return false
      end
    end
    return true
  end

  def move(dir, board)
    if can_move(dir, board)
      @members.each do |pos|
        x, y = pos
        nx, ny = moved_pos(x, y, dir)
        pos[0] = nx
        pos[1] = ny
      end
      @pivot = moved_pos(@pivot[0], @pivot[1], dir)
      return true
    else
      return false
    end
  end

  def can_turn(dir, board)
    @members.each do |x, y|
      nx, ny = rotated_pos(x, y, dir)
      if (nx < 0 || ny < 0 || ny >= board.size || nx >= board[0].size ||
          board[ny][nx])
        return false
      end
    end
    return true
{'fraction_non_alphanumeric': 0.1955096689609964, 'fraction_numerical': 0.02490986561783022, 'mean_word_length': 1.813739050253573, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '541326', 'n_tokens_mistral': 2719, 'n_tokens_neox': 2605, 'n_words': 745}