Files
work_cation/views/gamesViews.go
T

414 lines
9.2 KiB
Go

package views
import (
"bytes"
"context"
"errors"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"image"
"image/color"
"image/draw"
"image/png"
"slices"
"sync"
"work_cation/global"
"work_cation/models"
"work_cation/service"
)
func TenChinaGameView() {
game := NewTenGame(nil)
//go func() {
// for {
// select {
// case <-game.Ctx.Done():
// return
// case msg := <-global.Send.Game1Chan:
// game.play()
// }
// }
//}()
game.StartShow()
}
func StartGameListen() {
go func() {
for {
select {
case msg := <-global.Send.Game1Chan:
switch msg.Router {
case "/play_game":
g := global.GetGameInfo(msg.GameUuid)
if g != nil {
game := g.Obj.(*TenGame)
game.Play(game.userIndex, msg.Pos)
}
case "/close_game":
g := global.GetGameInfo(msg.GameUuid)
if g != nil {
dialog.NewConfirm("提示", "对方已退出", func(b bool) {
g.Obj.(*TenGame).w.Close()
}, g.Obj.(*TenGame).w).Show()
//dialog.ShowInformation("提示", "对方已退出", g.Obj.(*TenGame).w)
}
case "/start_game":
g := global.GetGameInfo(msg.GameUuid)
if g != nil {
fmt.Println("start_game")
continue
}
game := NewTenGame(nil)
online := &models.Online{ID: msg.User.ID, Ip: msg.User.Ip, Port: msg.User.Port}
game.AddNet(msg.GameUuid, 1, online, msg.User)
game.StartShow()
}
}
}
}()
}
// 格子棋 Checkered-Chess
type TenGame struct {
lock sync.Mutex // 玩家操作锁
playerMax int // 玩家总人数
playerID2Image map[int]fyne.Resource // 各个玩家对应的图标
defaultImage fyne.Resource // 默认图标
currentRoundPlayer int // 当前行动的玩家
players [][]int // 各个玩家已完成下棋数据
allMaps []int // 所有下棋数据
winCallback func(int) // 胜利返回
Items []*canvas.Image // 各个板块对象
w fyne.Window
itemSize fyne.Size //单个格子大小
itemX float32 // 格子总数x
itemY float32 // 格子总数y
winSum int // 胜利连线数
Ctx context.Context
Cancel func()
isNet bool // 是否是网络模式
uuid string
myIndex int
user *models.Users //
userIndex int
online *models.Online
}
func NewTenGame(winCallback func(int)) *TenGame {
playerImageMap := map[int]fyne.Resource{
0: theme.RadioButtonIcon(),
1: theme.CancelIcon(),
//2: theme.DownloadIcon(),
}
ctx, cancel := context.WithCancel(context.Background())
game := &TenGame{
playerID2Image: playerImageMap,
defaultImage: nil, //theme.ViewFullScreenIcon(),
currentRoundPlayer: 0,
players: make([][]int, len(playerImageMap)),
winCallback: winCallback,
itemSize: fyne.NewSize(50, 50),
itemX: 10,
itemY: 10,
winSum: 5,
Ctx: ctx,
Cancel: cancel,
isNet: false,
}
return game
}
func (t *TenGame) AddNet(uuid string, myIndex int, online *models.Online, user *models.Users) error {
t.isNet = true
t.online = online
t.myIndex = myIndex
t.user = user
t.uuid = uuid
info := &global.RunGameInfo{
Uuid: t.uuid,
Type: global.GameType1,
Obj: t,
}
global.CreateGame(info)
// 发起人
if myIndex == 0 {
t.userIndex = 1
_, err := service.Client.StartGame(t.online, t.uuid, global.GameType1)
if err != nil {
t.isNet = false
return err
}
}
return nil
}
func (t *TenGame) StartShow() {
myApp := fyne.CurrentApp()
myWindow := myApp.NewWindow("")
t.w = myWindow
t.setTitle()
wSize := fyne.NewSize(t.itemX*(t.itemSize.Width), t.itemY*(t.itemSize.Height))
myWindow.Resize(wSize)
myWindow.SetContent(container.New(layout.NewMaxLayout(),
canvas.NewImageFromResource(drawABackgroundImage(wSize, int(t.itemX), int(t.itemY))),
t.CanvasObject()))
myWindow.SetOnClosed(func() {
if t.isNet {
global.DeleteGame(t.uuid)
service.Client.CloseGame(t.online, t.uuid)
}
})
myWindow.CenterOnScreen()
myWindow.RequestFocus()
myWindow.Show()
}
func (t *TenGame) CanvasObject() fyne.CanvasObject {
var items []fyne.CanvasObject
for i := 0; i < int(t.itemX*t.itemY); i++ {
var itemIndex = i
image := canvas.NewImageFromResource(t.defaultImage)
image.FillMode = canvas.ImageFillOriginal
image.Resize(t.itemSize) //fy
t.Items = append(t.Items, image)
toggle := widget.NewButton("", func() {
err := t.Play(t.myIndex, itemIndex)
if err != nil {
dialog.ShowError(err, t.w)
}
})
toggle.Resize(t.itemSize)
iViews := container.NewBorder(nil, nil, nil, nil, container.NewWithoutLayout(image, toggle))
iViews.Resize(t.itemSize)
//gridWrap.Add()
items = append(items, iViews)
}
gridWrap := container.NewGridWithRows(int(t.itemX), items...)
return container.NewScroll(gridWrap)
}
func (t *TenGame) Play(userIndex int, pos int) error {
t.lock.Lock()
defer t.lock.Unlock()
if t.isNet && t.currentRoundPlayer != userIndex {
return errors.New("你急个der")
}
if slices.Contains(t.allMaps, pos) {
return errors.New("已经下过了")
}
if t.isNet && t.myIndex == userIndex {
// todo 广播数据
_, err := service.Client.PlayGame(t.online, t.uuid, pos)
if err != nil {
return err
}
}
// 下棋
t.allMaps = append(t.allMaps, pos)
t.play(pos)
// 推进
t.advance()
return nil
}
// 推进
func (t *TenGame) advance() {
t.currentRoundPlayer++
if t.currentRoundPlayer+1 > len(t.playerID2Image) {
t.currentRoundPlayer = 0
}
t.setTitle()
}
func (t *TenGame) setTitle() {
title := fmt.Sprintf("让我们来下棋 %0.f*%0.f win:%d", t.itemX, t.itemY, t.winSum)
if t.isNet {
title += fmt.Sprintf(" | 我 vs %s", t.user.Name)
if t.currentRoundPlayer == t.myIndex {
title += " | 我的回合"
} else {
title += " | 对手回合"
}
}
t.w.SetTitle(title)
}
/*
1, 2, 3
4, 5, 6
7, 8, 9
*/
func (t *TenGame) play(pos int) {
// 更新玩家数据
playerIntList := t.players[t.currentRoundPlayer]
playerIntList = append(playerIntList, pos)
t.players[t.currentRoundPlayer] = playerIntList
// 刷新展示
t.Items[pos].Resource = t.playerID2Image[t.currentRoundPlayer]
t.Items[pos].Refresh()
// check 胜利 判断
if t.winCheck(playerIntList, pos) {
endView := widget.NewButton("关闭", func() {
t.w.Close()
})
img := canvas.NewImageFromResource(t.playerID2Image[t.currentRoundPlayer])
img.FillMode = canvas.ImageFillOriginal
v := container.NewBorder(nil, endView, nil, nil,
container.NewBorder(nil, nil, img, nil, widget.NewLabel("胜利")))
dialog.NewCustomWithoutButtons("游戏结束", v, t.w).Show()
if t.winCallback != nil {
t.winCallback(t.currentRoundPlayer)
}
} else if len(t.allMaps) == int(t.itemX*t.itemY) {
endView := widget.NewButton("关闭", t.w.Close)
img := canvas.NewImageFromResource(t.playerID2Image[t.currentRoundPlayer])
img.FillMode = canvas.ImageFillOriginal
v := container.NewBorder(nil, endView, nil, nil,
container.NewBorder(nil, nil, img, nil, widget.NewLabel("平局")))
dialog.NewCustomWithoutButtons("游戏结束", v, t.w).Show()
}
}
func (t *TenGame) winCheck(playerIntList []int, pos int) bool {
//slices.Sort(playerIntList)
return IsArithmeticSequence2(playerIntList, pos, t.winSum, int(t.itemX), int(t.itemY))
}
/*
0, 1, 2
3, 4, 5
6, 7, 8
*/
type pos struct {
x int
y int
}
/*
{0, 0}, {1, 0}, {2, 0}
{0, 1}, {1, 1}, {2, 1}
{0, 2}, {1, 2}, {2, 2}
0 1 2
3 4 5
6 7 8
*/
func int3pos(maxX, i int) pos {
return pos{x: i % maxX, y: i / maxX}
}
func pos2int(maxX int, pos pos) int {
return pos.x + pos.y*maxX
}
var lineDirection = [][]pos{
{{1, 1}, {-1, -1}},
{{1, -1}, {-1, 1}},
{{1, 0}, {-1, 0}},
{{0, 1}, {0, -1}},
}
func IsArithmeticSequence2(numbers []int, in int, diff int, mapX, mapY int) bool {
if len(numbers) <= diff-1 {
return false
}
inPos := int3pos(mapX, in)
for _, direction := range lineDirection {
var sum = 0
for _, idirec := range direction {
sum += lineSum(inPos, mapX, mapY, numbers, idirec)
}
if sum >= diff-1 {
return true
}
}
return false
}
func lineSum(pos pos, mapX, mapY int, numbers []int, direction pos) (sum int) {
for {
pos.x += direction.x
pos.y += direction.y
if pos.x >= mapX || pos.y >= mapY || pos.x < 0 || pos.y < 0 {
return
}
if !slices.Contains(numbers, pos2int(mapX, pos)) {
return
}
sum++
}
}
// 画背景图
func drawABackgroundImage(size fyne.Size, itemX, itemY int) fyne.Resource {
img := image.NewRGBA(image.Rect(0, 0, int(size.Width), int(size.Height)))
draw.Draw(img, img.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
//itemX = itemX * 2
//itemY = itemY * 2
// 画横线
for ix := 1; ix < itemX; ix++ {
i1 := int(size.Height * (float32(ix) / float32(itemX)))
for x := 1; x < int(size.Width); x++ {
img.Set(x, i1, color.Black)
}
//ix++
}
// Draw vertical line
for ix := 1; ix < itemY; ix++ {
i2 := int(size.Width * (float32(ix) / float32(itemY)))
for y := 0; y < int(size.Height); y++ {
img.Set(i2, y, color.Black)
}
//ix++
}
buffer := new(bytes.Buffer)
err := png.Encode(buffer, img)
if err != nil {
return nil
}
return &fyne.StaticResource{
StaticName: "",
StaticContent: buffer.Bytes(),
}
}