一些 Ruby 代码片段2 趣味代码
一些琐碎的代码片段,没有固定的主题。
部分代码有问题,注意检查一下。
算 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.each_with_index.reject { |_, idx| idx == i || idx == j }.map(&:first)
new_expr = expr.each_with_index.reject { |_, idx| idx == i || idx == j }.map(&:first)
['+', '-', '*', '/'].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])
a = expr[i].is_a?(Integer) ? expr[i].to_s : "(#{expr[i]})"
b = expr[j].is_a?(Integer) ? expr[j].to_s : "(#{expr[j]})"
new_expr.push "#{a}#{op}#{b}"
result = calc24(new_nums, new_expr)
return result if result
new_nums.pop
new_expr.pop
end
end
end
false
end
end
p calc24([6, 6, 8, 8]) # "(6*8)/(8-6)"
p calc24([1, 2, 3, 4]) # "4*(3+(1+2))"
p calc24([1, 5, 5, 5]) # "5*(5-(1/5))"
p calc24([3, 3, 8, 8]) # "8/(3-(8/3))"
p calc24([1, 3, 4, 6]) # "6/(1-(3/4))"
数独
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
黑白棋
# 初始化棋盘
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
moves = get_valid_moves(board, current_player)
if moves.empty?
return -minimax(board, depth, -beta, -alpha, !maximizing_player)
end
best_eval = -Float::INFINITY
moves.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
八皇后
# 八皇后问题 - 递归回溯解法(找到一个解即停止)
# queen_pos[i] = j 表示第 i 行的皇后放在第 j 列
def solve_queen(row, queen_pos)
n = queen_pos.length
if row == n
print_board(queen_pos)
return true
end
n.times do |col|
if is_valid(row, col, queen_pos)
queen_pos[row] = col
return true if solve_queen(row + 1, queen_pos)
queen_pos[row] = -1
end
end
false
end
def is_valid(row, col, queen_pos)
row.times do |i|
return false if queen_pos[i] == col || (row - i).abs == (col - queen_pos[i]).abs
end
true
end
def print_board(solution)
solution.each do |col|
puts (['.'] * solution.length).tap { |r| r[col] = 'Q' }.join(' ')
end
end
queen_positions = [-1] * 8
solve_queen(0, queen_positions)
汉诺塔
def hanoi(n, from, to, aux)
if n == 1
puts "移动盘子 1 从 #{from} 到 #{to}"
else
hanoi(n - 1, from, aux, to)
puts "移动盘子 #{n} 从 #{from} 到 #{to}"
hanoi(n - 1, aux, to, from)
end
end
# 示例:3个盘子,从 A 柱移动到 C 柱,B 柱作为辅助
hanoi(3, 'A', 'C', 'B')
递归思路:先将 n-1 个盘子从 from 移到 aux,然后将第 n 个盘子从 from 移到 to,最后将 n-1 个盘子从 aux 移到 to
走迷宫
# 字符串迷宫 + BFS + parent 回溯路径
maze = <<~MAZE.split("\n")
#########
#S..#...#
###.#.#.#
#.....#.#
#.#####.#
#......E#
#########
MAZE
dirs = [[-1,0],[1,0],[0,-1],[0,1]]
start = nil
goal = nil
# 找到起点 S 和终点 E
maze.each_with_index { |r,i| r.chars.each_with_index { |c,j| start = [i,j] if c == ?S; goal = [i,j] if c == ?E } }
# BFS 核心(用 parent 记录前驱)
queue = [start]
parent = {}
visited = [start]
while !queue.empty?
x, y = queue.shift
break if [x, y] == goal
dirs.each do |dx, dy|
nx, ny = x + dx, y + dy
if nx.between?(0, maze.size-1) && ny.between?(0, maze[0].size-1) && maze[nx][ny] != ?# && !visited.include?([nx, ny])
visited << [nx, ny]
parent[[nx, ny]] = [x, y]
queue << [nx, ny]
end
end
end
# 从 E 回溯到 S 生成路径
path = []
curr = goal
while curr
path << curr
curr = parent[curr]
end
path.reverse!
# 输出
puts "完整路径坐标:"
p path
# 画迷宫
path.each { |x,y| maze[x][y] = ?* unless maze[x][y] == ?S || maze[x][y] == ?E }
puts "\n迷宫路径:"
puts maze.map { |l| l.gsub('#', '█') }
推箱子
参考
- Ruby Quiz https://rubyquiz.com/
- Rosetta Code https://rosettacode.org/wiki/Category:Ruby
- LeetCode https://leetcode.com/