Compare commits

..

11 Commits

Author SHA1 Message Date
c36d278a31 Fix Github workflow and add screenshots
Some checks failed
Go / build (push) Has been cancelled
2025-10-03 11:52:19 +01:00
Syed Daanish
9fe289a0ab Try simpler release
Some checks failed
Go / build (push) Has been cancelled
2025-02-09 15:34:12 +03:00
Syed Daanish
42b295413c All releases
Signed-off-by: Syed Daanish <syed@sixzix.com>
2025-02-09 15:24:26 +03:00
Syed Daanish
7dc26cf28a Add support for pre-releases 2025-02-09 15:23:29 +03:00
Syed Daanish
88d58abc29 Add github release auto binary 2025-02-09 15:22:04 +03:00
Syed Daanish
2c7b50e018 Re-add go.sum for github workflow 2025-02-09 15:13:40 +03:00
Syed Daanish
6f88306941 Fix go version 2025-02-09 15:12:18 +03:00
Syed Daanish
5d8fe93ffb Create go.yml 2025-02-09 15:10:58 +03:00
Syed Daanish
d6495310c4 Fixes 2025-02-09 15:10:52 +03:00
Syed Daanish
c01083f9a9 Add License and readme
Signed-off-by: Syed Daanish <syed@sixzix.com>
2025-02-09 15:07:39 +03:00
Syed Daanish
0e211e15be Fixes 2025-02-09 15:00:17 +03:00
10 changed files with 189 additions and 20 deletions

19
.github/workflows/go.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Go
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'
- name: Build
run: go build -v ./...

24
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Build and Release Go Binary
on:
release:
types: [created]
permissions:
contents: write
packages: write
# TODO: enable for macos and windows
jobs:
release-linux-amd64:
name: release linux/amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: linux
goarch: amd64

2
.gitignore vendored
View File

@@ -14,3 +14,5 @@
# Dependency directories
/Godeps/
go-mines

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Syed
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

54
README.md Normal file
View File

@@ -0,0 +1,54 @@
# Minesweeper (Terminal Edition)
A terminal-based Minesweeper game written in Go, using the `tcell` library for handling terminal UI and events.
## Features
- Classic Minesweeper gameplay
- Intuitive keyboard controls
- Terminal-based UI
- Real-time updates with `tcell`
## Some screenshots
![image1](./images/1.png)
![image2](./images/2.png)
![image3](./images/3.png)
## Installation
### Prerequisites
- Go 1.18+ installed
### Steps
1. Clone this repository:
```sh
git clone https://github.com/Toprun123/go-mines.git
cd go-mines
```
2. Install dependencies:
```sh
go get ./...
```
3. Build the project:
```sh
go build -o go-mines
```
4. Run the game:
```sh
./go-mines
```
## Controls
- `Arrow Keys` - Move cursor
- `Enter` - Reveal cell
- `F` - Flag/Unflag cell
- `Q` - Quit game
Or Mouse Clicks (Left Click to Reveal, Right Click to Flag/Unflag)
## Contributing
Feel free to open issues or submit pull requests!
## Author
Syed - https://github.com/Toprun123

4
go.mod
View File

@@ -1,10 +1,10 @@
module github.com/Toprun123/go-mines
go 1.23.4
go 1.23
require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.7.4 // indirect
github.com/gdamore/tcell/v2 v2.7.4 // direct
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.4.3 // indirect

BIN
images/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
images/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
images/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

85
main.go
View File

@@ -16,7 +16,7 @@ import (
var start, pline int
var offx, offy int
var fx, fy, sx, sy, mines int
var fx, fy, sx, sy, ax, ay, mines int
var screen tcell.Screen
var gameTime time.Time
var timr int
@@ -27,6 +27,7 @@ var mouseThing string
var asciiDigits map[rune][]string
var gameOver bool
var flags int
var board [][]cell
type cell struct {
err bool
@@ -35,6 +36,7 @@ type cell struct {
reveal bool
isMine bool
color string
sel bool
}
func main() {
@@ -116,6 +118,8 @@ func main() {
pline = 7
fx = -1
fy = -1
ax = 0
ay = 0
mines = *m_o
sx = *sx_o
sy = *sy_o
@@ -132,7 +136,7 @@ func main() {
}
offx = width/2 - (sx*5)/2
offy = height/2 - (sy*3-3)/2
board, _ := gen_board(sx, sy, mines, fx, fy)
board, _ = gen_board(sx, sy, mines, fx, fy)
refresh(board)
screen.Show()
go func() {
@@ -145,15 +149,54 @@ func main() {
ev := screen.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
if ev.Rune() == 'l' || ev.Rune() == 'q' {
switch ev.Rune() {
case 'l', 'q':
return
case 'f':
if !gameOver {
_ = flag_it(board, ax, ay, sx, sy)
}
case 'r', ' ':
if !gameOver {
_ = reveal(board, ax, ay, sx, sy)
}
}
if ev.Key() == tcell.KeyCtrlC {
switch ev.Key() {
case tcell.KeyCtrlC:
return
case tcell.KeyLeft:
board[ay][ax].sel = false
ax--
board[ay][ax].sel = true
refresh(board)
case tcell.KeyRight:
board[ay][ax].sel = false
ax++
board[ay][ax].sel = true
refresh(board)
case tcell.KeyUp:
board[ay][ax].sel = false
ay--
board[ay][ax].sel = true
refresh(board)
case tcell.KeyDown:
board[ay][ax].sel = false
ay++
board[ay][ax].sel = true
refresh(board)
case tcell.KeyEnter:
if fx == -1 && fy == -1 {
fx = ax
fy = ay
board, _ = gen_board(sx, sy, mines, fx, fy)
gameTime = time.Now()
}
if !gameOver {
_ = reveal(board, ax, ay, sx, sy)
}
}
case *tcell.EventMouse:
x, y := ev.Position()
doit := true
button := buttonify(ev.Buttons())
if x >= offx+(sx*5/2)-3 && x <= offx+(sx*5/2)+3 && y >= offy-4 && y <= offy-3 && button == "L" {
fx = -1
@@ -164,14 +207,13 @@ func main() {
board, _ := gen_board(sx, sy, mines, fx, fy)
refresh(board)
screen.Show()
doit = false
continue
}
x = x - offx
y = y - offy
boardX := int(math.Floor(float64(x) / 5))
boardY := int(math.Floor(float64(y) / 3))
if fx == -1 && fy == -1 && (button == "L" || button == "R") && doit {
if fx == -1 && fy == -1 && (button == "L" || button == "R") {
fx = boardX
fy = boardY
board, _ = gen_board(sx, sy, mines, fx, fy)
@@ -346,12 +388,12 @@ func flag_it(board [][]cell, x, y, width, height int) error {
}
func gen_board(width, height, mines, fx, fy int) ([][]cell, error) {
board := make([][]cell, height)
for i := range board {
board[i] = make([]cell, width)
board2 := make([][]cell, height)
for i := range board2 {
board2[i] = make([]cell, width)
}
if mines >= width*height {
return board, errors.New("too many mines")
return board2, errors.New("too many mines")
}
source := rand.NewSource(time.Now().UnixNano())
rand := rand.New(source)
@@ -359,17 +401,20 @@ func gen_board(width, height, mines, fx, fy int) ([][]cell, error) {
for mineCount < mines {
x := rand.Intn(width)
y := rand.Intn(height)
if (x == fx && y == fy) || board[y][x].isMine {
if (x == fx && y == fy) || board2[y][x].isMine {
continue
}
board[y][x].isMine = true
board2[y][x].isMine = true
mineCount++
}
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
if board[y][x].isMine {
if board2[y][x].isMine {
continue
}
if ax == x && ay == y {
board2[y][x].sel = true
}
directions := []struct{ dx, dy int }{
{-1, -1}, {0, -1}, {1, -1},
{-1, 0}, {1, 0},
@@ -378,11 +423,11 @@ func gen_board(width, height, mines, fx, fy int) ([][]cell, error) {
count := 0
for _, dir := range directions {
nx, ny := x+dir.dx, y+dir.dy
if nx >= 0 && nx < width && ny >= 0 && ny < height && board[ny][nx].isMine {
if nx >= 0 && nx < width && ny >= 0 && ny < height && board2[ny][nx].isMine {
count++
}
}
board[y][x].mines = count
board2[y][x].mines = count
colors := map[int]string{
0: "#41e8b0",
1: "#7cc7ff",
@@ -394,10 +439,10 @@ func gen_board(width, height, mines, fx, fy int) ([][]cell, error) {
7: "#999999",
8: "#d0d8e0",
}
board[y][x].color = colors[board[y][x].mines]
board2[y][x].color = colors[board2[y][x].mines]
}
}
return board, nil
return board2, nil
}
func print_board(board [][]cell, solved int) {
@@ -461,6 +506,10 @@ func print_board(board [][]cell, solved int) {
style = tcell.StyleDefault.Background(tcell.GetColor("#384048")).Foreground(tcell.GetColor("#1f272f")).Bold(true)
style2 = tcell.StyleDefault.Background(tcell.GetColor("#384048")).Foreground(tcell.GetColor(board[i][j].color)).Bold(true)
}
// Chaange color of border if selected
if board[i][j].sel {
style = style.Foreground(tcell.GetColor("#ff7d7d"))
}
print_at(j*5+offx, i*3+1+offy, fmt.Sprint(" ", symbol, " "), style2)
draw_box(j*5+offx, i*3+offy, j*5+4+offx, i*3+2+offy, style, chars_i)
}