From 7f387f101534447d3ac2e4be82ff3585c100648a Mon Sep 17 00:00:00 2001
From: Yerlotic
Date: Wed, 25 Mar 2026 11:52:12 +0000
Subject: [PATCH] Quality of life fixes
Add arrow keys support
Fix arrow misalignment by using different Unicode characters
Prevent players from killing themselves by doing a 180 in a single frame
Don't update arrow direction before the next frame
Make the dimensions closer to 16:9
Don't use snap in `sshtron.sh` if it's not available
---
.gitignore | 3 ++
README.md | 2 +-
game.go | 90 +++++++++++++++++++++++++++++++++++------------
sshtron.sh | 11 ++++--
static/index.html | 2 +-
5 files changed, 81 insertions(+), 27 deletions(-)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bc6dd4d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+id_rsa
+id_rsa.pub
+sshtron
diff --git a/README.md b/README.md
index 39b0fff..b79bb25 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ SSHTron is a multiplayer lightcycle game that runs through SSH. Just run the com
$ ssh sshtron.zachlatta.com
-_Controls: WASD or vim keybindings to move (**do not use your arrow keys**). Escape or Ctrl+C to exit._
+_Controls: WASD, arrow keys or vim keybindings to move. Escape or Ctrl+C to exit._

diff --git a/game.go b/game.go
index 7577475..3b53e15 100644
--- a/game.go
+++ b/game.go
@@ -45,7 +45,7 @@ func (h *Hub) Run(g *Game) {
h.Sessions[s] = struct{}{}
case s := <-h.Unregister:
if _, ok := h.Sessions[s]; ok {
- fmt.Fprint(s, "\r\n\r\n~ End of Line ~ \r\n\r\nRemember to use WASD to move!\r\n\r\n")
+ fmt.Fprint(s, "\r\n\r\n ~ Bye! ~ \r\n\r\n")
// Unhide the cursor
fmt.Fprint(s, "\033[?25h")
@@ -77,18 +77,26 @@ func (p Position) RoundY() int {
type PlayerDirection int
const (
- verticalPlayerSpeed = 0.007
- horizontalPlayerSpeed = 0.01
+ speedMultiplier = 1
+ verticalPlayerSpeed = 0.007 * speedMultiplier
+ horizontalPlayerSpeed = 0.01 * speedMultiplier
playerCountScoreMultiplier = 1.25
playerTimeout = 15 * time.Second
- playerUpRune = '⇡'
- playerLeftRune = '⇠'
- playerDownRune = '⇣'
- playerRightRune = '⇢'
+ //playerUpRune = '⇡'
+ //playerLeftRune = '⇠'
+ //playerDownRune = '⇣'
+ //playerRightRune = '⇢'
+ playerUpRune = '↑'
+ playerLeftRune = '←'
+ playerDownRune = '↓'
+ playerRightRune = '→'
+
+ //playerTrailHorizontal = '╌'
+ //playerTrailVertical = '┆'
+ playerTrailHorizontal = '─'
+ playerTrailVertical = '│'
- playerTrailHorizontal = '┄'
- playerTrailVertical = '┆'
playerTrailLeftCornerUp = '╭'
playerTrailLeftCornerDown = '╰'
playerTrailRightCornerDown = '╯'
@@ -145,6 +153,7 @@ type Player struct {
Name string
CreatedAt time.Time
Direction PlayerDirection
+ InitDir PlayerDirection
Marker rune
Color color.Attribute
Pos *Position
@@ -172,6 +181,7 @@ func NewPlayer(s *Session, worldWidth, worldHeight int,
CreatedAt: time.Now(),
Marker: playerDownRune,
Direction: PlayerDown,
+ InitDir: PlayerDown,
Color: color,
Pos: &Position{startX, startY},
}
@@ -192,38 +202,34 @@ func (p *Player) calculateScore(delta float64, playerCount int) float64 {
}
func (p *Player) HandleUp() {
- if p.Direction == PlayerDown {
+ if p.InitDir == PlayerDown {
return
}
p.Direction = PlayerUp
- p.Marker = playerUpRune
p.s.didAction()
}
func (p *Player) HandleLeft() {
- if p.Direction == PlayerRight {
+ if p.InitDir == PlayerRight {
return
}
p.Direction = PlayerLeft
- p.Marker = playerLeftRune
p.s.didAction()
}
func (p *Player) HandleDown() {
- if p.Direction == PlayerUp {
+ if p.InitDir == PlayerUp {
return
}
p.Direction = PlayerDown
- p.Marker = playerDownRune
p.s.didAction()
}
func (p *Player) HandleRight() {
- if p.Direction == PlayerLeft {
+ if p.InitDir == PlayerLeft {
return
}
p.Direction = PlayerRight
- p.Marker = playerRightRune
p.s.didAction()
}
@@ -249,6 +255,21 @@ func (p *Player) Update(g *Game, delta float64) {
// If we moved, add a trail segment.
if endX != startX || endY != startY {
+ // Update initial direction
+ // Needed to prevent players from doing a 180 in one frame
+ p.InitDir = p.Direction
+ // Update player direction only after a new frame
+ switch p.InitDir {
+ case PlayerUp:
+ p.Marker = playerUpRune
+ case PlayerLeft:
+ p.Marker = playerLeftRune
+ case PlayerDown:
+ p.Marker = playerDownRune
+ case PlayerRight:
+ p.Marker = playerRightRune
+ }
+
var lastSeg *PlayerTrailSegment
var lastSegX, lastSegY int
if len(p.Trail) > 0 {
@@ -320,7 +341,7 @@ type Tile struct {
}
const (
- gameWidth = 78
+ gameWidth = 82
gameHeight = 22
keyW = 'w'
@@ -337,6 +358,12 @@ const (
keyK = 'k'
keyL = 'l'
+ // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#list-of-keyboard-strings
+ keyUp = 'A'
+ keyDown = 'B'
+ keyRight = 'C'
+ keyLeft = 'D'
+
keyComma = ','
keyO = 'o'
keyE = 'e'
@@ -417,15 +444,34 @@ func (gm *GameManager) HandleNewChannel(c ssh.Channel, color string) {
fmt.Println(err)
break
}
+ if r == keyEscape {
+ has_more := reader.Buffered()
+ // not just escape
+ if has_more > 0 {
+ r1, _, err := reader.ReadRune()
+ if err != nil {
+ fmt.Println(err)
+ break
+ }
+ // Then goes arrow key
+ if r1 == '[' {
+ r, _, err = reader.ReadRune()
+ if err != nil {
+ fmt.Println(err)
+ break
+ }
+ }
+ }
+ }
switch r {
- case keyW, keyZ, keyK, keyComma:
+ case keyUp, keyW, keyZ, keyK, keyComma:
session.Player.HandleUp()
- case keyA, keyQ, keyH:
+ case keyLeft, keyA, keyQ, keyH:
session.Player.HandleLeft()
- case keyS, keyJ, keyO:
+ case keyDown, keyS, keyJ, keyO:
session.Player.HandleDown()
- case keyD, keyL, keyE:
+ case keyRight, keyD, keyL, keyE:
session.Player.HandleRight()
case keyCtrlC, keyEscape:
if g.SessionCount() == 1 {
diff --git a/sshtron.sh b/sshtron.sh
index 416c663..607965b 100755
--- a/sshtron.sh
+++ b/sshtron.sh
@@ -1,7 +1,12 @@
#!/bin/sh
-cd "$SNAP_DATA"
+if [ -d "$SNAP_DATA" ]; then
+ cd "$SNAP_DATA"
+ [ -f id_rsa ] || "$SNAP"/usr/bin/ssh-keygen -t rsa -N '' -f id_rsa
-[ -f id_rsa ] || $SNAP/usr/bin/ssh-keygen -t rsa -N '' -f id_rsa
+ sshtron > "$SNAP_DATA/sshtron.log" 2>&1
+else
+ [ -f id_rsa ] || ssh-keygen -t rsa -N '' -f id_rsa
-sshtron > "$SNAP_DATA/sshtron.log" 2>&1
+ ./sshtron > sshtron.log 2>&1
+fi
diff --git a/static/index.html b/static/index.html
index e17ffde..89d045d 100644
--- a/static/index.html
+++ b/static/index.html
@@ -29,7 +29,7 @@ SSH Tron
be playing in seconds.
$ ssh sshtron.zachlatta.com
- (WASD or vim keybindings for movement, do not use arrow keys)
+ (WASD, arrow keys or vim keybindings for movement)
By @MaxWofford and