Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fragoulis committed Jan 2, 2024
1 parent b3f754d commit 5d44751
Show file tree
Hide file tree
Showing 12 changed files with 708 additions and 588 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.trunk
*.log
main
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
TARGET=main
PACKAGE_PATH=.

.PHONY: build
build:
go build -o ${TARGET} ${PACKAGE_PATH}

.PHONY: run
run: build
./${TARGET}

.PHONY: clean
clean:
go clean

.PHONY: test
test:
go test ./...

.PHONY: lint
lint:
golangci-lint run --enable-all

.PHONE: deps
deps:
go install github.com/golangci/golangci-lint/cmd/[email protected]
go install github.com/cosmtrek/air@latest

.PHONY: up
up: build
air --build.cmd "make build" --build.bin "./${TARGET}" --build.delay "100" \
--build.exclude_dir "" \
--build.include_ext "go, tpl, tmpl, html, css, scss, js, ts, sql, jpeg, jpg, gif, png, bmp, svg, webp, ico" \
--misc.clean_on_exit "true"

.PHONY: tidy
tidy:
go fmt ./...
go mod tidy -v

.PHONY: audit
audit:
go mod verify
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@latest -checks=all,-ST1000,-U1000 ./...
go run golang.org/x/vuln/cmd/govulncheck@latest ./...
go test -race -buildvcs -vet=off ./...
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Termtris

Ever wander what a terminal based tetris game would look like?

Started playing around with low level terminal programming in Go in order understand more about the terminal and I endded up working on a tetris game.
7 changes: 7 additions & 0 deletions frame.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

type Frame struct {
Width int
Height int
Data []rune
}
239 changes: 239 additions & 0 deletions game.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package main

import (
"errors"
"fmt"
"sync"
"time"

tcell "github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
)

type Game struct {
quitq chan struct{}
screen tcell.Screen
eventq chan tcell.Event
errmsg string
quitone sync.Once
leftView *views.ViewPort
centerView *views.ViewPort
rightView *views.ViewPort
grid *Grid
sprites []*Sprite
active *Sprite
pause bool
spawnPiece chan bool
dropPiece chan bool
speed time.Duration

sync.Mutex
}

func (g *Game) Init() error {
var err error
g.screen, err = tcell.NewScreen()
if err != nil {
return err
}

err = g.screen.Init()
if err != nil {
return err
}

defaultStyle := tcell.StyleDefault.
Background(tcell.ColorBlack).
Foreground(tcell.ColorWhite)

g.screen.SetStyle(defaultStyle)

g.screen.EnableMouse()

screenWidth, screenHeight := g.screen.Size()
sidebarWidth := (screenWidth - MainWidth) / 2

g.grid = NewGrid(MainWidth, screenHeight)

g.leftView = views.NewViewPort(g.screen, 0, 0, sidebarWidth, screenHeight)
g.centerView = views.NewViewPort(g.screen, sidebarWidth, 0, MainWidth, screenHeight)
g.rightView = views.NewViewPort(g.screen, sidebarWidth+MainWidth, 0, sidebarWidth, screenHeight)

g.quitq = make(chan struct{})
g.eventq = make(chan tcell.Event)
g.spawnPiece = make(chan bool, 1)
g.dropPiece = make(chan bool, 1)

g.speed = 100 * time.Millisecond

g.Reset()

return nil
}

func (g *Game) Run() error {
go g.EventPoller()
go g.Updater()

loop:
for {
g.Draw()
select {
case <-g.quitq:
// log.Printf("quit 79\n")
break loop
case <-time.After(time.Millisecond * 10):
case ev := <-g.eventq:
g.HandleEvent(ev)
}
}

// Inject a wakeup interrupt
iev := tcell.NewEventInterrupt(nil)
g.screen.PostEvent(iev)

g.screen.Fini()
// wait for updaters to finish
if g.errmsg != "" {
return errors.New(g.errmsg)
}
return nil
}

func (g *Game) Error(msg string) {
g.errmsg = msg
g.Quit()
}

func (g *Game) Quit() error {
g.quitone.Do(func() {
close(g.quitq)
})
return nil
}

func (g *Game) Reset() {
g.grid.Clear()

g.sprites = []*Sprite{
NewSprite(),
}
g.active = g.sprites[0]
}

func (g *Game) Draw() {
g.Lock()
defer g.Unlock()

g.grid.Clear()
for _, sprite := range g.sprites {
sprite.Draw(g.grid)
}

g.screen.Clear()
g.leftView.Fill('.', tcell.StyleDefault.
Background(tcell.ColorPurple).
Foreground(tcell.ColorWhite))
g.rightView.Fill('.', tcell.StyleDefault.
Background(tcell.ColorPurple).
Foreground(tcell.ColorWhite))
g.grid.Draw(g.centerView)
g.screen.Show()
}

func (g *Game) EventPoller() {
for {
select {
case <-g.quitq:
// log.Printf("quit 140\n")
return
default:
}
ev := g.screen.PollEvent()
if ev == nil {
return
}
select {
case <-g.quitq:
// log.Printf("quit 150\n")
return
case g.eventq <- ev:
}
}
}

func (g *Game) HandleEvent(ev tcell.Event) bool {
switch ev := ev.(type) {
case *tcell.EventResize:
// g.lview.Resize(0, 1, -1, -1)
// g.sview.Resize(0, 0, -1, 1)
// g.level.HandleEvent(ev)
case *tcell.EventKey:
if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
fmt.Printf("Pressing quit\n")
g.Quit()
return true
}
if ev.Key() == tcell.KeyEnter {
g.pause = !g.pause
return true
}
if ev.Key() == tcell.KeyUp {
if !g.pause && g.grid.CanPieceMove(g.active) {
g.active.NextFrame()
}
return true
}
if ev.Key() == tcell.KeyDown {
// g.dropPiece <- true
g.speed = time.Millisecond * 10
return true
}
if ev.Key() == tcell.KeyLeft {
if !g.pause && g.grid.CanPieceMoveLeft(g.active) {
g.active.PosX -= 1
}
return true
}
if ev.Key() == tcell.KeyRight {
if !g.pause && g.grid.CanPieceMoveRight(g.active) {
g.active.PosX += 1
}
return true
}
}

return true
}

func (g *Game) Updater() {
for {
select {
case <-g.quitq:
// log.Printf("quit 204\n")
return
case <-time.After(g.speed):
g.Lock()
// log.Printf("updating 1\n")
// g.level.Update(time.Now())

if !g.pause {
if g.grid.CanPieceMoveVertically(g.active) {
g.active.PosY += 1
} else {
g.SpawnPiece()
g.speed = time.Millisecond * 100
}
}

g.Unlock()
}
}
}

func (g *Game) SpawnPiece() {
// log.Printf("spawn piece\n")
sprite := NewSprite()
g.sprites = append(g.sprites, sprite)
g.active = sprite
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ module terminal

go 1.19

require github.com/gdamore/tcell/v2 v2.6.0

require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell/v2 v2.6.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/term v0.5.0 // indirect
Expand Down
5 changes: 0 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCyS
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
Expand All @@ -27,7 +23,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
Loading

0 comments on commit 5d44751

Please sign in to comment.