鴻蒙小案例-五子棋
1.準備組件(組件布局)
2.下棋功能實現(xiàn)
3.機器人下棋功能實現(xiàn)
4.贏棋功能實現(xiàn)
5.附屬功能實現(xiàn)
剛開始以為挺簡單的,越寫越…emo
因為代碼有點多,所以這里就簡單講下邏輯,文末貼上代碼
邏輯只是我個人想的,不代表只有這一種實現(xiàn)方式,有其他想法可以在下面留言
另外功能做的比較簡單,有一些沒實現(xiàn),但是基本功能都有,大家可以自行優(yōu)化
1.組件的準備
組件就比較簡單,采用Canvas就可以,畫出一個棋盤的布局
畫布是一個300*300的,所以可下棋點位都可以計算出來
2.功能
1.前置狀態(tài)校驗
校驗是否開始游戲,棋子顏色是否選擇,沒有選擇給出彈窗提醒
2.校正觸摸點的坐標
觸摸點的坐標肯定不是精準的數(shù)字,要把它校正為符合棋盤規(guī)則的坐標
比如 53.666666666=》50,采用四舍五入方式
3.校驗當前修正后的坐標點位是否有棋子
那么之前每一步的棋子要先加入一個全局變量,黑色和白色的還要再次單獨區(qū)分并加入單獨的變量
在繪制棋子
4.判斷人工下棋完成后是否贏棋
因為我們的畫布長寬都是固定的,所以每一個坐標點位都可以推斷出來
在這里我們計算當前坐標橫向,縱向,西北,東北四個方向,每個方向5種贏棋情況的坐標點位
每個方向的5種贏棋情況應為:
當前坐標分別處于第一位,第二位,第三位,第四位,第五位時,如下圖
以上的情況已經(jīng)包含了所有的贏棋情況
當所有坐標點位拿到后,判斷每個方向的4個坐標點是否全部在已落子坐標數(shù)據(jù)變量中,如果有一個方向是,就贏,1個都沒有那就繼續(xù)下棋
(這點比較難理解,可以參考代碼實現(xiàn))
5.機器下棋
當走到這一步時肯定沒有贏棋了,那么就該繼續(xù)下棋了
首先需要獲取到人工落子坐標周圍的8個坐標點,結合實時更新的已落子坐標點位,拿到人工坐標周邊的空余可下棋坐標點位,隨機(這里是決定機器人智能程度的關鍵,我只做了最簡單的)生成1個位置,繪制坐標
繪制完后,還要判斷機器人的是否贏棋,調(diào)用方法即可
到這里功能基本都完了
6.小功能實現(xiàn)
開始游戲/重新開始:清理棋盤
白棋/黑棋:選中人工下棋的棋色
悔棋:結合已落子坐標后退兩步
結束游戲:清理棋盤
認輸:清理棋盤
demo寫的比較簡單,但鑒于時間關系還是有一些功能沒實現(xiàn),寫在下面,有需要的可以自行優(yōu)化
待完成功能:
1.判斷贏棋的條件不精準:有時候(偶發(fā))4個也會顯示勝利
2.剛下完的棋子要給加亮處理
3.悔棋功能的書寫
4.棋子最外邊一層,不允許落子
5.贏棋后,需要加亮顯示贏棋的那一條線
6.機器人的智能不高,需要判斷對方3個或者雙3的情況去堵它
針對第六點,大家可以接入AI大模型,實時對戰(zhàn)更刺激
當然也可以改造成聯(lián)網(wǎng),真人對戰(zhàn)的,那這里就要用到數(shù)據(jù)庫了,每下一步棋都要同步到數(shù)據(jù)庫,并且要同步到正在下棋的兩個人那里,這個就看大家的精力了
完整代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-832636.html
import promptAction from '@ohos.promptAction';
@Entry
@Preview
@Component
struct Wuziqi {
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
/**
* 待完成功能:
* 1,判斷贏棋的條件不精準:有時候4個也會顯示勝利
* 2.剛下完的棋子要給加亮處理
* 3.悔棋功能的書寫
* 4.棋子最外邊一層,不允許落子
* 5.贏棋后,需要加亮顯示贏棋的那一條線
* 6.AI的智能不高,需要判斷對方3個或者雙3的情況去堵它
*/
//棋盤寬度
qpWidth: number = 300
//棋盤高度
qpHeight: number = 300
//白旗下棋坐標集合,悔棋使用
@State
hqzbList: qpzbClass[] = []
//黑旗下棋坐標集合,悔棋使用
@State
bqzbList: qpzbClass[] = []
//已經(jīng)占用的坐標集合
@State
comzbList: qpzbClass[] = []
//棋盤允許下棋的坐標點集合
@State
qpZbList: qpzbClass[] = []
//是否開始游戲,游戲進行狀態(tài)
@State
isStatus: boolean = false
//當前用戶選擇的 棋色,''代表初始,還未選擇顏色
@State
nowColor: Color.White | Color.Black | '' = ''
//棋盤內(nèi)網(wǎng)格線的顏色,黑色
qpLineColor: string = Color.Black
//角標坐標集合
jbList: qpzbClass[] = [{ x: 60, y: 60 }, { x: 200, y: 60 }, { x: 60, y: 200 }, { x: 200, y: 200 }]
//當前棋子的坐標
@State
zb: qpzbClass = { x: 0, y: 0 }
//贏棋的常量
isWinPD(tszb: qpzbClass[]) {
let x: number = this.zb.x
let y: number = this.zb.y
//橫向
let conZbList1: qpzbClass[] = [{ x: x + 20, y: y }, { x: x + 40, y: y }, { x: x + 60, y: y }, { x: x + 80, y: y }]
let conZbList2: qpzbClass[] = [{ x: x - 20, y: y }, { x: x + 20, y: y }, { x: x + 40, y: y }, { x: x + 60, y: y }]
let conZbList3: qpzbClass[] = [{ x: x - 40, y: y }, { x: x - 20, y: y }, { x: x + 20, y: y }, { x: x + 40, y: y }]
let conZbList4: qpzbClass[] = [{ x: x - 60, y: y }, { x: x - 40, y: y }, { x: x - 20, y: y }, { x: x + 20, y: y }]
let conZbList5: qpzbClass[] = [{ x: x - 80, y: y }, { x: x - 60, y: y }, { x: x - 40, y: y }, { x: x - 20, y: y }]
//豎向
let conZbList6: qpzbClass[] = [{ x: x, y: y + 20 }, { x: x, y: y + 40 }, { x: x, y: y + 60 }, { x: x, y: y + 80 }]
let conZbList7: qpzbClass[] = [{ x: x, y: y - 20 }, { x: x, y: y + 20 }, { x: x, y: y + 40 }, { x: x, y: y + 60 }]
let conZbList8: qpzbClass[] = [{ x: x, y: y - 40 }, { x: x, y: y - 20 }, { x: x, y: y + 20 }, { x: x, y: y + 40 }]
let conZbList9: qpzbClass[] = [{ x: x, y: y - 60 }, { x: x, y: y - 40 }, { x: x, y: y - 20 }, { x: x, y: y + 20 }]
let conZbList10: qpzbClass[] = [{ x: x, y: y - 80 }, { x: x, y: y - 60 }, { x: x, y: y - 40 }, { x: x, y: y - 20 }]
//西北
let conZbList11: qpzbClass[] = [{ x: x + 20, y: y + 20 }, { x: x + 40, y: y + 40 }, { x: x + 60, y: y + 60 }, {
x: x + 80,
y: y + 80
}]
let conZbList12: qpzbClass[] = [{ x: x - 20, y: y - 20 }, { x: x + 20, y: y + 20 }, { x: x + 40, y: y + 40 }, {
x: x + 60,
y: y + 60
}]
let conZbList13: qpzbClass[] = [{ x: x - 40, y: y - 40 }, { x: x - 20, y: y - 20 }, { x: x + 20, y: y + 20 }, {
x: x + 40,
y: y + 40
}]
let conZbList14: qpzbClass[] = [{ x: x - 60, y: y - 60 }, { x: x - 40, y: y - 40 }, { x: x - 20, y: y - 20 }, {
x: x + 20,
y: y + 20
}]
let conZbList15: qpzbClass[] = [{ x: x - 80, y: y - 80 }, { x: x - 60, y: y - 60 }, { x: x - 40, y: y - 40 }, {
x: x - 20,
y: y - 20
}]
//東北
let conZbList16: qpzbClass[] = [{ x: x - 20, y: y + 20 }, { x: x - 40, y: y + 40 }, { x: x - 60, y: y + 60 }, {
x: x - 80,
y: y + 80
}]
let conZbList17: qpzbClass[] = [{ x: x + 20, y: y - 20 }, { x: x - 20, y: y + 20 }, { x: x - 40, y: y + 40 }, {
x: x - 60,
y: y + 60
}]
let conZbList18: qpzbClass[] = [{ x: x + 40, y: y - 40 }, { x: x + 20, y: y - 20 }, { x: x - 20, y: y + 20 }, {
x: x - 40,
y: y + 40
}]
let conZbList19: qpzbClass[] = [{ x: x + 60, y: y - 60 }, { x: x + 40, y: y - 40 }, { x: x + 20, y: y - 20 }, {
x: x - 20,
y: y + 20
}]
let conZbList20: qpzbClass[] = [{ x: x + 80, y: y - 80 }, { x: x + 60, y: y - 60 }, { x: x + 40, y: y - 40 }, {
x: x + 20,
y: y + 20
}]
//獲取這4位坐標是否全部存在 存在,即獲勝,整個for循環(huán)直接停掉,結束,
// AlertDialog.show({message:'tszb:'+JSON.stringify(tszb)+';conZbList1:'+JSON.stringify(conZbList1)})
if (this.arr1Containarr2(tszb, conZbList1) || this.arr1Containarr2(tszb, conZbList2) ||
this.arr1Containarr2(tszb, conZbList3) || this.arr1Containarr2(tszb, conZbList4)
|| this.arr1Containarr2(tszb, conZbList5) || this.arr1Containarr2(tszb, conZbList6)
|| this.arr1Containarr2(tszb, conZbList7) || this.arr1Containarr2(tszb, conZbList8)
|| this.arr1Containarr2(tszb, conZbList9) || this.arr1Containarr2(tszb, conZbList10)
|| this.arr1Containarr2(tszb, conZbList11) || this.arr1Containarr2(tszb, conZbList12)
|| this.arr1Containarr2(tszb, conZbList13) || this.arr1Containarr2(tszb, conZbList14)
|| this.arr1Containarr2(tszb, conZbList15) || this.arr1Containarr2(tszb, conZbList16)
|| this.arr1Containarr2(tszb, conZbList17) || this.arr1Containarr2(tszb, conZbList18)
|| this.arr1Containarr2(tszb, conZbList19) || this.arr1Containarr2(tszb, conZbList20)
) {
return true
}
return false
}
//判斷是否贏棋
isWin(zb: qpzbClass, color: string) {
//需要判斷4個方向,每個方向5種情況的 贏棋情況
//當前需要用到的顏色的棋子的坐標集合
let tszb: qpzbClass[] = color === Color.White ? this.bqzbList : this.hqzbList
if (this.isWinPD(tszb)) {
AlertDialog.show({ message: color === Color.White ? '白棋勝利' : '黑棋勝利' })
this.isStatus = false
return true
}
return false
}
//判斷1個數(shù)組是否全部存在另一個數(shù)組中
arr1Containarr2(arr1: qpzbClass[], arr2: qpzbClass[]) {
if (arr2.length > 1) {
for (let i: number = 0; i < arr2.length; i++) {
if (!this.arr1Containarr2(arr1, [arr2[i]])) {
return false
}
}
} else {
for (let i: number = 0; i < arr1.length; i++) {
if (JSON.stringify(arr1[i]) === JSON.stringify(arr2[0])) {
return true
}
}
return false
}
return true
}
//校驗下棋的前置狀態(tài)
checkBeforeStatus() {
//校驗各項狀態(tài)
if (this.nowColor === '') {
AlertDialog.show({ message: '請先選擇棋色' })
return false
}
if (!this.isStatus) {
AlertDialog.show({ message: '未開始游戲' })
return false
}
return true
}
//下棋操作
running(qz: qpzbClass) {
//計算觸摸點的坐標點為 最近的可落子的坐標點
qz = this.conuntNowZb(qz)
this.zb = qz
//校驗當前坐標是否有棋子
if (this.checkZb(qz)) {
promptAction.showToast({ message: '點擊無效' })
return
}
//記錄棋子的坐標,分別記錄黑白色的
this.nowColor === Color.White ? this.bqzbList.push(qz) : this.hqzbList.push(qz)
this.drowQz(qz, this.nowColor)
if (!this.isWin(qz, this.nowColor)) {
//機器人下棋
this.aiRunning(qz)
}
}
//校驗當前坐標是否有棋子
checkZb(qz: qpzbClass) {
let st: boolean = false
this.comzbList.some(ev => {
if (JSON.stringify(ev) === JSON.stringify(qz)) {
st = true
return true
}
})
return st
}
//機器人下棋
aiRunning(qz: qpzbClass) {
//獲取當前人工棋子周圍可落子坐標點,需要去除已經(jīng)落子的坐標
let pZb: qpzbClass[] = this.countPeripheryZb(qz)
let random: number = Math.floor(Math.random() * pZb.length)
let p: qpzbClass = pZb[random]
this.drowQz(p, this.nowColor === Color.White ? Color.Black : Color.White)
this.zb = p
if (!this.isWin(p, this.nowColor)) {
this.nowColor === Color.White ? this.hqzbList.push(p) : this.bqzbList.push(p)
}
}
//獲取一個坐標的8個周邊 坐標點
getZbZb(qz: qpzbClass) {
let x: number = qz.x
let y: number = qz.y
let pZb: qpzbClass[] = [{ x: x - 20, y: y }, { x: x + 20, y: y }, { x: x - 20, y: y - 20 }, { x: x, y: y - 20 },
{ x: x + 20, y: y - 20 }, { x: x - 20, y: y + 20 }, { x: x, y: y + 20 }, { x: x + 20, y: y + 20 }]
return pZb
}
//獲取當前人工棋子周圍 可落子坐標點
countPeripheryZb(qz: qpzbClass) {
//固定寫死的周邊 坐標點
let pZb: qpzbClass[] = this.getZbZb(qz)
//校驗位置是否有棋子
let duplicates: qpzbClass[] = [...pZb]
for (let i = duplicates.length - 1; i >= 0; i--) {
// 檢查當前A數(shù)組的子數(shù)組是否在B數(shù)組中存在
let found = this.comzbList.some(subB => JSON.stringify(subB) === JSON.stringify(duplicates[i]));
if (found) {
duplicates.splice(i, 1);
}
}
return duplicates
}
//計算觸摸點的坐標點為 最近的可落子的坐標點
conuntNowZb(qz: qpzbClass) {
return { x: Math.round(Math.round(qz.x) / 20) * 20, y: Math.round(Math.round(qz.y) / 20) * 20 }
}
//繪制角標
drowJb() {
for (let index: number = 0; index < this.jbList.length; index++) {
this.context.beginPath()
let x: number = this.jbList[index].x
let y: number = this.jbList[index].y
this.context.arc(x, y, 4, 0, 10)
this.context.strokeStyle = this.qpLineColor //角標邊框顏色
this.context.stroke()
this.context.closePath()
}
}
//繪制棋子
drowQz(qz: qpzbClass, color: string) {
this.context.beginPath()
this.context.arc(qz.x, qz.y, 8, 0, 10)
this.context.fillStyle = color
this.context.fill() //填充顏色
this.context.strokeStyle = Color.YELLOW//棋子邊框顏色
this.context.stroke()
this.context.closePath()
//記錄已經(jīng)占用的棋子位置
this.comzbList.push(qz)
}
//計算棋盤內(nèi)可落子的 坐標點
countZb(i: number) {
for (let i2 = 1; i2 <= 13; i2++) {
this.qpZbList.push({ x: i2 * 20, y: i * 20 })
}
}
//棋盤繪制
qpCreate() {
this.context.beginPath();
//設置棋盤內(nèi)網(wǎng)格線的顏色
this.context.strokeStyle = this.qpLineColor
this.context.fill()
//橫向棋線
for (let i: number = 1; i <= 15; i++) {
this.context.moveTo(0, i * 20);
this.context.lineTo(this.qpWidth - 20, i * 20);
this.context.stroke();
this.countZb(i)
}
//縱向棋線
for (let i: number = 1; i <= 15; i++) {
this.context.moveTo(i * 20, 0);
this.context.lineTo(i * 20, this.qpHeight - 20);
this.context.stroke();
}
this.context.closePath();
//設置角標
this.drowJb()
}
//棋盤,棋子,清除
clearQp() {
this.hqzbList = []
this.bqzbList = []
this.comzbList = []
this.context.clearRect(0, 0, this.qpWidth, this.qpHeight) //清理畫布內(nèi)繪畫
this.qpCreate() //重新繪制棋盤
}
@Styles
buttonStyle_height20(){
.height(20)
.backgroundColor(Color.White)
}
@Styles
buttonStyle_height35(){
.height(35)
.backgroundColor(Color.White)
}
@Styles
rowPadding(){
.padding({
top: 20,
bottom: 20
})
}
build() {
Column() {
//上功能區(qū)
Row({ space: 20 }) {
Button('白棋')
.buttonStyle_height20()
.fontWeight(FontWeight.Bold)
.fontSize(25)
.fontColor(Color.Black)
.onClick(() => {
if (this.nowColor === '' || this.comzbList.length<1) {
this.nowColor = Color.White
}else{
promptAction.showToast({message:'點擊無效'})
}
})
.backgroundColor(this.nowColor === Color.White ? Color.YELLOW : '')
Button(this.nowColor!=='' && this.comzbList.length>1?'重新開始':'開始游戲')
.buttonStyle_height35()
.fontWeight(FontWeight.Bold)
.fontSize(35)
.fontColor(Color.Black)
.onClick(() => {
this.isStatus = true
this.clearQp()
})
Button('黑棋')
.buttonStyle_height20()
.fontWeight(FontWeight.Bold)
.fontSize(25)
.fontColor(Color.Black)
.onClick(() => {
if (this.nowColor === '' || this.comzbList.length<1 ) {
this.nowColor = Color.Black
}else{
promptAction.showToast({message:'點擊無效'})
}
})
.backgroundColor(this.nowColor === Color.Black ? Color.YELLOW : '')
}.justifyContent(FlexAlign.SpaceBetween).height(50)
//棋盤位置
Row() {
Column() {
Canvas(this.context) {
}
.onTouch((event: TouchEvent) => {
//按下時觸發(fā),就近坐標點,繪制一個圓塊
if (event.type === TouchType.Up) {
if (this.checkBeforeStatus()) {
this.running({ x: event.touches[0].x, y: event.touches[0].y })
}
}
})
.onReady(() => {
//設置繪畫邊框?qū)挾?/span>
this.context.lineWidth = 1
this.qpCreate()
})
.backgroundColor(Color.Yuanm)
.margin({
top: 15,
bottom: 15
})
.border({
width: 1,
color: Color.Black
})
}
.width(this.qpWidth)
.height(this.qpHeight)
.border({
width: 5,
color: Color.Black,
radius: 1,
style: BorderStyle.Solid
})
.padding({
left: 15,
right: 15
})
}
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
.width('100%')
.height(450)
.rowPadding()
.backgroundColor(Color.Yuanm)
//下功能區(qū)
Row({ space: 20 }) {
Button('悔棋')
.buttonStyle_height20()
.fontWeight(FontWeight.Bold)
.fontSize(25)
.fontColor(Color.Black)
.onClick(() => {
//悔棋操作
AlertDialog.show({message:'功能待開發(fā)'})
})
Button('結束游戲')
.buttonStyle_height35()
.fontWeight(FontWeight.Bold)
.fontSize(35)
.fontColor(Color.Black)
.onClick(() => {
//結束游戲
this.isStatus = false
//清理棋盤,重新繪制棋盤
this.clearQp()
})
Button('認輸')
.buttonStyle_height20()
.fontWeight(FontWeight.Bold)
.fontSize(25)
.fontColor(Color.Black)
.onClick(() => {
//認輸
this.isStatus = false
//清理棋盤,重新繪制棋盤
this.clearQp()
})
}.justifyContent(FlexAlign.SpaceBetween).height(50)
}
.width('100%')
.height('100%')
.rowPadding()
}
}
class qpzbClass {
x: number = 0
y: number = 0
}
//顏色枚舉
enum Color {
Black = '#000000',
White = '#FFFFFF',
Yuanm = 'RGB(245,173,96)',
YELLOW = 'RGB(218, 165, 32)'
}
代碼可能稍微有點亂,沒有時間去做精細的優(yōu)化,本來就是個小demo,湊合著用吧-文章來源地址http://www.zghlxwxcb.cn/news/detail-832636.html
到了這里,關于鴻蒙小案例-五子棋的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!