分享一个五子棋AI算法,完整代码, 核心代码不到300行
主要思路:落子时, 遍历棋盘上所有空白位置,算出该点落子的得分,并保存下来, 最后算出最大得分的落子位置,出现多个相同得分时, 随机选择一个。
评分主要类型见代码中 socre_type枚举定义
AI强度大约为中等,和普通人对弈, 大约80%胜率。
带AI对弈的完整lua代码:
local sid = 0
local function id(initial)sid = initial and initial or sid + 1return sid
endscore_type = {fazhan2 = id(), -- 发展2fazhan3x = id(), -- 一端被阻挡的3zudang2 = id(), -- 阻挡2fazhan3 = id(), -- 两端无阻挡的3zudang3 = id(), -- 阻挡对方形成的双向无阻挡的3fazhan4x = id(), -- 发展一端阻挡的4fazhan4 = id(), -- 发展无阻挡的4zudang4 = id(), -- 阻挡对方单封堵的4five = id(), -- 五子连珠
}size = 15 -- 棋盘大小
board = {}-- 评分相等时,计算坐标离中心的距离
-- TODO, 优化为同时考虑坐标周围无其他方向的对手棋子
function center(i,j)return math.abs(size/2-i) + math.abs(size/2-j)
end-- 计算指定坐标下t类型的棋子的得分
function score(board, t, i, j)local function get_score(left, right, t)local line = {}for _, v in pairs(left) do table.insert(line, v)endtable.insert(line, t)for _, v in pairs(right) do table.insert(line, v)end-- local print = function() end-- 五个的情况for k=1, #line-4 do if line[k]==t and line[k+1]==t and line[k+2]==t and line[k+3]==t and line[k+4]==t thenprint(i,j, "five")return score_type.fiveendend-- 阻挡四for k=1, #line-4 do local diff_count = 0local same_count = 0 for i=k, k+4 do same_count = same_count + (line[i] == t and 1 or 0)diff_count = diff_count + (line[i] == -t and 1 or 0)endif diff_count == 4 and same_count == 1 thenreturn score_type.zudang4endend-- 四个的情况local s = 4==(#left) and 2 or 1for k=s, #left+1 do if line[k]==t and line[k+1]==t and line[k+2]==t and line[k+3]==t then -- 能形成连续的四个-- 左端有无阻挡local lx,rx = false, falseif not line[k-1] or line[k-1] ~= 0 thenlx = trueend-- 右端有无阻挡if not line[k+4] or line[k+4] ~= 0 thenrx = trueendif not (lx and rx) thenif (not lx) and (not rx) thenreturn score_type.fazhan4elsereturn score_type.fazhan4xendendend end--中间有一个空白的四个 和 一端被阻挡的四个 同分for k=1, #left+1 do local same_count = 0local zero_count = 0for i=k, k+4 do same_count = same_count + (line[i] == t and 1 or 0)zero_count = zero_count + (line[i] == 0 and 1 or 0)endif same_count == 4 and zero_count == 1 thenreturn score_type.fazhan4xendend-- 三个的情况s = #left+1-2if s<0 then s=1end-- 阻挡3local t_idx = #left+1for k=1, #left+1 do if k<=t_idx and k+3>=t_idx thenlocal diff_count = 0local same_count = 0 for i=k, k+3 do same_count = same_count + (line[i] == t and 1 or 0)diff_count = diff_count + (line[i] == -t and 1 or 0)endif diff_count == 3 and same_count == 1 then-- 有一端已经挡住了的都不算if (line[k-1] and line[k-1] == t ) or (line[k+4] and line[k+4] == t) thenelsereturn score_type.zudang3endendendendfor k=s, #left+1 do -- 连续的三个if line[k] == t and line[k+1]==t and line[k+2]==t then-- 阻挡local lx,rx = false, falseif line[k-1] and line[k-1] ~= 0 thenlx = trueend-- 右端有无阻挡if line[k+3] and line[k+3] ~= 0 thenrx = trueendif not (lx and rx) thenif (not lx) and (not rx) thenreturn score_type.fazhan3elsereturn score_type.fazhan3xendendendends=s-1if s<=0 then s = 1 endfor k=s, #left+1 do local same_count = 0local zero_count = 0for i=k, k+3 do same_count = same_count + (line[i] == t and 1 or 0)zero_count = zero_count + (line[i] == 0 and 1 or 0)endif same_count == 3 and zero_count == 1 then -- 有一段已经挡住了的都不算if (not line[k-1] or line[k-1] == -t ) or (not line[k+4] or line[k+4] == -t) thenelsereturn score_type.fazhan3endendend-- 两个的情况-- 阻挡2local t_idx = #left+1for k=1, #left+1 do if k<=t_idx and k+3>=t_idx thenlocal diff_count = 0local same_count = 0 for i=k, k+3 do same_count = same_count + (line[i] == t and 1 or 0)diff_count = diff_count + (line[i] == -t and 1 or 0)endif diff_count == 2 and same_count == 1 then-- 有一端已经挡住了的都不算if (line[k-1] and line[k-1] == t ) or (line[k+4] and line[k+4] == t) thenelsereturn score_type.zudang2endendendend-- 发展2return 0end-- 左右各取最多四个坐标的值local function get_line(ldx,ldy,rdx,rdy)local left = {}local right = {}local c = 4for k=c, 1, -1 do local v = board[i+ldx*k] and board[i+ldx*k][j+ldy*k]if v thentable.insert(left, v)endendfor k=1, c do local v = board[i+rdx*k] and board[i+rdx*k][j+rdy*k]if v thentable.insert(right, v)endendreturn left, rightendlocal scores = {}-- 横向local l, r = get_line(-1,0,1,0)scores[1] = get_score(l,r,t)-- 纵向l,r = get_line(0,-1,0,1)scores[2] = get_score(l,r,t)-- 斜线l,r = get_line(-1,1,1,-1)scores[3] = get_score(l,r,t)-- 反斜线l,r = get_line(-1,-1,1,1)scores[4] = get_score(l,r,t)table.sort(scores, function(a,b) return a > b end )return scores
endfunction drop(board, t)local ss = {} -- 所有可下点的评分和下注点for i=1, size do for j=1, size do if board[i][j] == 0 thens = score(board, t,i,j)table.insert(ss, {s=s,i=i,j=j})endendend-- 棋局已经满了if #ss == 0 then return 0 end-- 按照评分排序table.sort(ss, function(a,b)for k=1, 4 do if a.s[k] == b.s[k] thenif a.s[k] == 0 thenreturn center(a.i, a.j) < center(b.i, b.j)endelseif a.s[k] > b.s[k] thenreturn trueelsereturn falseendendend)local chooses = {ss[1]}-- 增加AI随机性local function is_same_score(s1, s2)if s1 and s2 thenfor k=1, 4 do if s1.s[k] ~= s2.s[k] then return falseendendreturn trueendendfor k=1, #ss-1 do if ss[k].s[1] > 0 and is_same_score(ss[k], ss[k+1]) thentable.insert(chooses, ss[k+1])elsebreakendendlocal choose = chooses[math.random(#chooses)]local i,j = choose.i, choose.jboard[i][j] = tif choose.s[1] == score_type.five thenreturn i,j,trueendreturn i,j
end-- 初始化棋盘
function init_board()for i=1, size do board[i] = {}for j=1, size do board[i][j] = 0endend
endfunction print_board() for i=1, size do str = ""for j=1, size do local t = board[i][j]if t == 0 then str =str.." . "elseif t == 1 then str = str.. " □ "elseif t == -1 then str = str.." ○ "endendprint(str.."n")end
endfunction play() t = 1step = 1while true do print(step.."-----------------------------------")-- 类型为t的 下棋i,j, over = drop(board, t)if i== 0 thenprint("棋盘已满")break end-- 打印棋牌状况print_board()if over then breakend-- 换人t = -t -- 步数+1step = step+1end
endinit_board()
play()
本文发布于:2024-02-04 07:36:07,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170702145153589.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |