一些琐碎的代码片段,没有固定的主题。

部分代码有问题,注意检查一下。

算 24

def calc24(nums, expr = nums)
  if nums.length == 1
    nums[0] == 24? expr[0] : false
  else
    nums.length.times do |i|
      nums.length.times do |j|
        next if i == j
        new_nums = nums.select_with_index{|_, idx| idx!=i && idx!=j}
        new_expr = expr.select_with_index{|_, idx| idx!=i && idx!=j}
        ['+', '-', '*', '/'].each do |op|
          next if op==:'/' && nums[j]==0
          next if (op==:'+' || op==:'*') && i>j
          new_nums.push nums[i].to_r.send(op, nums[j])
          new_expr.push "(#{expr[i]})#{op}(#{expr[j]})"
        end
        x = calc24(new_nums, new_expr) and return x
        new_nums.pop
        new_expr.pop
      end
    end
  end
  nil
end

p calc24([6, 6, 8, 8]) # "((6*8)/(9-7))"

数独

TODO: 代码有问题,检查一下。

def is_valid(board, row, col, num)
    # 检查行
    (0..8).each do |i|
      return false if board[row][i] == num
    end
    # 检查列
    (0..8).each do |i|
      return false if board[i][col] == num
    end
    # 检查3x3子网格
    start_row = (row / 3) * 3
    start_col = (col / 3) * 3
    (0..2).each do |i|
      (0..2).each do |j|
        return false if board[start_row + i][start_col + j] == num
      end
    end
    true
  end
  
  def solve_sudoku(board)
    (0..8).each do |row|
      (0..8).each do |col|
        if board[row][col] == 0
          (1..9).each do |num|
            if is_valid(board, row, col, num)
              board[row][col] = num
              if solve_sudoku(board)
                return true
              else
                board[row][col] = 0
              end
            end
          end
          return false
        end
      end
    end
    true
  end
  
  # 示例数独棋盘,0表示空白单元格
  sudoku_board = [
    [5, 3, 0, 0, 7, 0, 0, 0, 0],
    [6, 0, 0, 1, 9, 5, 0, 0, 0],
    [0, 9, 8, 0, 0, 0, 0, 6, 0],
    [8, 0, 0, 0, 6, 0, 0, 0, 3],
    [4, 0, 0, 8, 0, 3, 0, 0, 1],
    [7, 0, 0, 0, 2, 0, 0, 0, 6],
    [0, 6, 0, 0, 0, 0, 2, 8, 0],
    [0, 0, 0, 4, 1, 9, 0, 0, 5],
    [0, 0, 0, 0, 8, 0, 0, 7, 9]
  ]
  
  if solve_sudoku(sudoku_board)
    sudoku_board.each do |row|
      puts row.join(' ')
    end
  else
    puts "该数独无解"
  end

黑白棋

TODO: 代码有问题,检查一下。

# 初始化棋盘
def init_board
  board = Array.new(8) { Array.new(8, 0) }
  board[3][3] = 1
  board[3][4] = -1
  board[4][3] = -1
  board[4][4] = 1
  board
end

# 打印棋盘,标记可用移动的编号
def print_board(board, moves)
  puts "="*18
  puts "| X: #{board.flatten.select(&:positive?).size}  O: #{board.flatten.select(&:negative?).size}"
  puts "+"+("-"*17)+"+"
  move_index = 1
  8.times do |i|
    row = "|"
    8.times do |j|
      if moves.include?([i, j])
        row += "%2d"%move_index
        
        move_index += 1
      else
        case board[i][j]
        when 1
          row += ' X'
        when -1
          row += ' O'
        else
          row += '  '
        end
      end
    end
    row += ' |'
    puts row
  end
  puts "+"+("-"*17)+"+"
end

# 判断是否在棋盘内
def is_on_board(x, y)
  x >= 0 && x < 8 && y >= 0 && y < 8
end

# 获取有效落子位置
def get_valid_moves(board, player)
  moves = []
  8.times do |x|
    8.times do |y|
      if board[x][y] == 0 && is_valid_move(board, player, x, y)
        moves << [x, y]
      end
    end
  end
  moves
end

# 判断落子是否有效
def is_valid_move(board, player, x, y)
  return false if board[x][y] != 0
  opponent = -player
  directions = [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]
  directions.each do |dx, dy|
    new_x, new_y = x + dx, y + dy
    if is_on_board(new_x, new_y) && board[new_x][new_y] == opponent
      new_x += dx
      new_y += dy
      while is_on_board(new_x, new_y) && board[new_x][new_y] != 0
        if board[new_x][new_y] == player
          return true
        end
        new_x += dx
        new_y += dy
      end
    end
  end
  false
end

# 执行落子
def make_move(board, player, x, y)
  board[x][y] = player
  opponent = -player
  directions = [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]
  directions.each do |dx, dy|
    # p [dx, dy]
    new_x, new_y = x + dx, y + dy
    # tiles_to_flip = []
    if is_on_board(new_x, new_y) && board[new_x][new_y] == opponent
      tiles_to_flip = [[new_x, new_y]]
      new_x += dx
      new_y += dy
      while is_on_board(new_x, new_y) && board[new_x][new_y] != 0
        #p [:a,tiles_to_flip,[x,y],[new_x,new_y],player]
        if board[new_x][new_y] == player
          #p x: tiles_to_flip # , board:board
          tiles_to_flip.each do |tx, ty|
            #p tx:tx,ty:ty
            board[tx][ty] = player
          end
          break
        end
        tiles_to_flip << [new_x, new_y]
        new_x += dx
        new_y += dy
      end
    end
  end
end

# 位置权重矩阵
WEIGHT_MATRIX = [
  [120, -20, 20, 5, 5, 20, -20, 120],
  [-20, -40, -5, -5, -5, -5, -40, -20],
  [20, -5, 15, 3, 3, 15, -5, 20],
  [5, -5, 3, 3, 3, 3, -5, 5],
  [5, -5, 3, 3, 3, 3, -5, 5],
  [20, -5, 15, 3, 3, 15, -5, 20],
  [-20, -40, -5, -5, -5, -5, -40, -20],
  [120, -20, 20, 5, 5, 20, -20, 120]
]

# 计算得分,加上位置权重
def calculate_score(board)
  score = 0
  8.times do |i|
    8.times do |j|
      cell = board[i][j]
      score += cell * WEIGHT_MATRIX[i][j]
    end
  end
  score
end

# Minimax 算法 with Alpha - Beta 剪枝
def minimax(board, depth, alpha, beta, maximizing_player)
  current_player = maximizing_player ? 1 : -1
  if depth == 0 || get_valid_moves(board, 1).empty? && get_valid_moves(board, -1).empty?
    return current_player*calculate_score(board)
  end
  best_eval = -Float::INFINITY
  get_valid_moves(board, current_player).each do |move|
    new_board = board.map(&:dup)
    make_move(new_board, current_player, move[0], move[1])
    eval_score = -minimax(new_board, depth - 1, -beta, -alpha, !maximizing_player)
    best_eval = [best_eval, eval_score].max
    alpha = [alpha, eval_score].max
    break if beta <= alpha
  end
  return best_eval
end

# 获取最佳移动
def get_best_move(board, depth)
  best_score = -Float::INFINITY
  best_move = nil
  get_valid_moves(board, 1).shuffle.each do |move|
    new_board = board.map(&:dup)
    make_move(new_board, 1, move[0], move[1])
    score = -minimax(new_board, depth, -Float::INFINITY, Float::INFINITY, false)
    # puts "位置 #{move} 打分 #{score}"
    if score > best_score
      best_score = score
      best_move = move
    end
  end
  best_move
end

# 游戏主循环
def play_game
  board = init_board
  loop do
    player_moves = get_valid_moves(board, -1)
     print_board(board, player_moves)
    if player_moves.empty?
      puts "玩家无可用移动,电脑继续。"
    else
      loop do
        puts "请输入你的移动编号 (1 - #{player_moves.size}): "
        # input = gets.chomp.to_i
        input = rand(1..player_moves.size)
        puts "> #{input}"
        if (1..player_moves.size).include?(input)
          x, y = player_moves[input-1]
          make_move(board, -1, x, y)
          break
        else
          puts "无效编号,请重新输入。"
        end
      end
    end
    computer_moves = get_valid_moves(board, 1)
    print_board(board, computer_moves)
    if computer_moves.empty?
      puts "电脑无可用移动,游戏结束。"
      break
    end
    computer_move = get_best_move(board, 3+1-1 )
    make_move(board, 1, computer_move[0], computer_move[1])
    puts "电脑移动\n> #{computer_moves.index(computer_move)+1}"
  end
end

play_game if __FILE__ == $PROGRAM_NAME

八皇后

TODO: 代码有问题,检查一下。

class EightQueens
  def initialize(n = 8)
    @n = n
    @solutions = []
  end

  def solve
    board = Array.new(@n) { Array.new(@n, 0) }
    backtrack(board, 0)
    @solutions
  
    end
  end

  private

  def backtrack(board, col)
    if col == @n
      solution = []
      board.each_with_index do |row, i|
        row.each_with_index do |cell, j|
          solution << [i, j] if cell == 1
        end
      end
      @solutions << solution
      return
    end

    (0...@n).each do |row|
      if is_safe(board, row, col)
        board[row][col] = 1
        backtrack(board, col + 1)
        board[row][col] = 0
      end
    end
  end

  def is_safe(board, row, col)
 
    (0...col).each do |i|
      return false if board[row][i] == 1
    end
 
    (row - 1).downto(0).zip((col - 1).downto(0)) do |i, j|
      return false if board[i][j] == 1
    end
 
    ((row + 1)...@n).zip((col - 1).downto(0)) do |i, j|
      return false if board[i][j] == 1
    end

    true
  end
end

# 使用示例
queens = EightQueens.new
solutions = queens.solve
puts "找到 #{solutions.length} 种解决方案:"
solutions.each_with_index do |solution, index|
  puts "解决方案 #{index + 1}:"
  solution.each do |queen|
    puts "皇后位置: (#{queen[0]}, #{queen[1]})"
  end
  puts
end

汉诺塔



走迷宫

推箱子

参考

  • Ruby Quiz https://rubyquiz.com/
  • Rosetta Code https://rosettacode.org/wiki/Category:Ruby
  • LeetCode https://leetcode.com/