diff --git a/common_test.go b/common_test.go new file mode 100644 index 0000000..7299777 --- /dev/null +++ b/common_test.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + "testing" + "work_cation/views" +) + +func TestP1(t *testing.T) { + r := views.IsArithmeticSequence2([]int{0, 4, 8}, 8, 3, 3, 3) + fmt.Println(r) +} diff --git a/global/send.go b/global/send.go index f96882f..4dc9328 100644 --- a/global/send.go +++ b/global/send.go @@ -3,7 +3,11 @@ package global import "work_cation/models" type SendGlobal struct { - SendChan chan *models.Message + SendChan chan *models.Message + Game1Chan chan *models.GameMessage } -var Send = &SendGlobal{SendChan: make(chan *models.Message, 1000)} +var Send = &SendGlobal{ + SendChan: make(chan *models.Message, 1000), + Game1Chan: make(chan *models.GameMessage, 1000), +} diff --git a/models/gameMessage.go b/models/gameMessage.go new file mode 100644 index 0000000..1cb373a --- /dev/null +++ b/models/gameMessage.go @@ -0,0 +1,6 @@ +package models + +type GameMessage struct { + UserID string `json:"user_id"` + Pos int `json:"pos"` +} diff --git a/service/server.go b/service/server.go index 994f71b..3e0af7d 100644 --- a/service/server.go +++ b/service/server.go @@ -105,6 +105,28 @@ func (s *serverService) StartListenServer() error { c.JSON(200, gin.H{"message": "ok"}) }) + // 游戏接口 + router.POST("/game", func(c *gin.Context) { + uuid := c.GetHeader("User-UserID") + user := repo.UserFollow.GetUser(global.DB, uuid) + if user.Ip != c.ClientIP() { + c.JSON(200, gin.H{"message": "对方未关注你"}) + return + } + var msg = make(map[string]interface{}) + if err := c.ShouldBind(&msg); err != nil { + c.JSON(200, gin.H{"message": "输入异常"}) + return + } + + message := &models.GameMessage{ + UserID: user.ID, + Pos: msg["text"].(int), + } + global.Send.Game1Chan <- message + c.JSON(200, gin.H{"message": "ok"}) + }) + srv := &http.Server{ Addr: cfg.T.ServerAddr, Handler: router, diff --git a/views/gamesViews.go b/views/gamesViews.go index ea78680..3645d1e 100644 --- a/views/gamesViews.go +++ b/views/gamesViews.go @@ -1,6 +1,9 @@ package views import ( + "context" + "errors" + "fmt" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" @@ -13,18 +16,29 @@ import ( func TenChinaGameView() { game := NewTenGame(nil) + //go func() { + // for { + // select { + // case <-game.Ctx.Done(): + // return + // case msg := <-global.Send.Game1Chan: + // game.play() + // } + // } + //}() game.StartShow() } type TenGame struct { - lock sync.Mutex + lock sync.Mutex // 玩家操作锁 playerMax int // 玩家总人数 playerID2Image map[int]fyne.Resource // 各个玩家对应的图标 - defaultImage fyne.Resource + defaultImage fyne.Resource // 默认图标 - CurrentRoundPlayer int // 当前行动的玩家 + currentRoundPlayer int // 当前行动的玩家 players [][]int // 各个玩家已完成下棋数据 + allMaps []int // 所有下棋数据 winCallback func(int) // 胜利返回 @@ -34,38 +48,52 @@ type TenGame struct { itemSize fyne.Size //单个格子大小 itemX float32 // 格子总数x itemY float32 // 格子总数y + + winSum int // 胜利连线数 + + Ctx context.Context + Cancel func() } func NewTenGame(winCallback func(int)) *TenGame { - game := &TenGame{ - playerID2Image: map[int]fyne.Resource{ - 0: theme.RadioButtonIcon(), - 1: theme.CancelIcon(), - }, - defaultImage: theme.CheckButtonIcon(), + playerImageMap := map[int]fyne.Resource{ + 0: theme.RadioButtonIcon(), + 1: theme.CancelIcon(), + //2: theme.DownloadIcon(), + } - CurrentRoundPlayer: 0, - players: make([][]int, 2), + ctx, cancel := context.WithCancel(context.Background()) + game := &TenGame{ + playerID2Image: playerImageMap, + defaultImage: theme.CheckButtonIcon(), + + currentRoundPlayer: 0, + players: make([][]int, len(playerImageMap)), winCallback: winCallback, - itemSize: fyne.NewSize(100, 100), + itemSize: fyne.NewSize(25, 25), itemX: 3, itemY: 3, + winSum: 3, + Ctx: ctx, + Cancel: cancel, } return game } func (t *TenGame) StartShow() { myApp := fyne.CurrentApp() - myWindow := myApp.NewWindow("Game") + myWindow := myApp.NewWindow(fmt.Sprintf("Game %0.f*%0.f win:%d", t.itemX, t.itemY, t.winSum)) t.w = myWindow myWindow.SetContent(t.CanvasObject()) - myWindow.Resize(fyne.NewSize(t.itemX*t.itemSize.Width+20, t.itemY*t.itemSize.Height+20)) + myWindow.Resize(fyne.NewSize(t.itemX*(t.itemSize.Width+8), t.itemY*(t.itemSize.Height+8))) myWindow.CenterOnScreen() + t.w.SetOnClosed(func() { t.Cancel() }) myWindow.Show() } func (t *TenGame) CanvasObject() fyne.CanvasObject { - gridWrap := container.NewGridWrap(t.itemSize) + var items []fyne.CanvasObject + for i := 0; i < int(t.itemX*t.itemY); i++ { var itemIndex = i @@ -75,28 +103,34 @@ func (t *TenGame) CanvasObject() fyne.CanvasObject { t.Items = append(t.Items, image) toggle := widget.NewButton("", func() { - t.Play(0, itemIndex) + _ = t.Play(0, itemIndex) }) toggle.Resize(t.itemSize) - //toggle.Alignment = 3 - gridWrap.Add(container.NewBorder(nil, nil, nil, nil, container.NewWithoutLayout(image, toggle))) + iViews := container.NewBorder(nil, nil, nil, nil, container.NewWithoutLayout(image, toggle)) + //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.CurrentRoundPlayer != userIndex { - // return errors.New("not your turn") + //if t.currentRoundPlayer != userIndex { + // return errors.New("你急个der") //} + if slices.Contains(t.allMaps, pos) { + return errors.New("已经下过了") + } + // 下棋 + t.allMaps = append(t.allMaps, pos) t.play(pos) - // 推进 - t.CurrentRoundPlayer++ - if t.CurrentRoundPlayer+1 > len(t.playerID2Image) { - t.CurrentRoundPlayer = 0 + t.currentRoundPlayer++ + if t.currentRoundPlayer+1 > len(t.playerID2Image) { + t.currentRoundPlayer = 0 } return nil } @@ -108,53 +142,110 @@ func (t *TenGame) Play(userIndex int, pos int) error { */ func (t *TenGame) play(pos int) { // 更新玩家数据 - playerIntList := t.players[t.CurrentRoundPlayer] + playerIntList := t.players[t.currentRoundPlayer] playerIntList = append(playerIntList, pos) - t.players[t.CurrentRoundPlayer] = playerIntList + t.players[t.currentRoundPlayer] = playerIntList // 刷新展示 - t.Items[pos].Resource = t.playerID2Image[t.CurrentRoundPlayer] + t.Items[pos].Resource = t.playerID2Image[t.currentRoundPlayer] t.Items[pos].Refresh() // check 胜利 判断 - if t.winCheck(playerIntList) { + if t.winCheck(playerIntList, pos) { endView := widget.NewButton("关闭", func() { t.w.Close() }) - img := canvas.NewImageFromResource(t.playerID2Image[t.CurrentRoundPlayer]) + 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) + 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) bool { - slices.Sort(playerIntList) - if isArithmeticSequence(playerIntList, 1) || - isArithmeticSequence(playerIntList, 2) || - isArithmeticSequence(playerIntList, 3) || - isArithmeticSequence(playerIntList, 4) { - return true +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 isArithmeticSequence(numbers []int, diff int) bool { - if len(numbers) <= 2 { - return false - } - - for i := 1; i < len(numbers); i++ { - if numbers[i]-numbers[i-1] != diff { - 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++ } - - return true }