mirror of
https://codeberg.org/scip/golsky.git
synced 2025-12-16 20:20:57 +01:00
completed save rect to RLE file feature
This commit is contained in:
@@ -31,6 +31,7 @@ Based on: https://youtu.be/FWSR_7kZuYg?si=ix1dmo76D8AmF25F
|
|||||||
* you can paint your own patterns in the game
|
* you can paint your own patterns in the game
|
||||||
* the game can also be started with an empty grid, which is easier to paint patterns
|
* the game can also be started with an empty grid, which is easier to paint patterns
|
||||||
* wrap around grid mode can be enabled
|
* wrap around grid mode can be enabled
|
||||||
|
* you can also save rectangles of the grid to RLE files
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
|
|
||||||
@@ -78,6 +79,8 @@ While it runs, there are a couple of commands you can use:
|
|||||||
* move mouse while middle mouse button pressed: move canvas
|
* move mouse while middle mouse button pressed: move canvas
|
||||||
* escape: reset to 1:1 zoom
|
* escape: reset to 1:1 zoom
|
||||||
* s: save game state to file (can be loaded with -l)
|
* s: save game state to file (can be loaded with -l)
|
||||||
|
* c: enter copy mode. Mark a rectangle with the mouse, when you
|
||||||
|
release the mous button it is being saved to an RLE file
|
||||||
* q: quit
|
* q: quit
|
||||||
|
|
||||||
# Report bugs
|
# Report bugs
|
||||||
|
|||||||
5
grid.go
5
grid.go
@@ -69,6 +69,11 @@ func GetFilename(generations int64) string {
|
|||||||
return fmt.Sprintf("dump-%s-%d.gol", now.Format("20060102150405"), generations)
|
return fmt.Sprintf("dump-%s-%d.gol", now.Format("20060102150405"), generations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFilenameRLE(generations int64) string {
|
||||||
|
now := time.Now()
|
||||||
|
return fmt.Sprintf("rect-%s-%d.rle", now.Format("20060102150405"), generations)
|
||||||
|
}
|
||||||
|
|
||||||
func (grid *Grid) SaveState(filename string) error {
|
func (grid *Grid) SaveState(filename string) error {
|
||||||
file, err := os.Create(filename)
|
file, err := os.Create(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
13
main.go
13
main.go
@@ -16,6 +16,19 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// grid := [][]int64{
|
||||||
|
// {0, 1, 1},
|
||||||
|
// {0, 1, 0},
|
||||||
|
// {1, 1, 0},
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err := rle.StoreGridToRLE(grid, "test.rle", "B3/S23", 3, 3)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// os.Exit(0)
|
||||||
|
|
||||||
game := NewGame(config, Play)
|
game := NewGame(config, Play)
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
|
|||||||
80
rle/rle.go
80
rle/rle.go
@@ -1,8 +1,9 @@
|
|||||||
// source: https://github.com/nhoffmann/life by N.Hoffmann 2020.
|
// original source: https://github.com/nhoffmann/life by N.Hoffmann 2020.
|
||||||
package rle
|
package rle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -52,7 +53,7 @@ func (rle *RLE) partitionFile() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Invlaid input: Header is missing")
|
return fmt.Errorf("invalid input: Header is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rle *RLE) parseComments() error {
|
func (rle *RLE) parseComments() error {
|
||||||
@@ -98,3 +99,78 @@ func removeWhitespace(input string) string {
|
|||||||
re := regexp.MustCompile(` *\t*\r*\n*`)
|
re := regexp.MustCompile(` *\t*\r*\n*`)
|
||||||
return re.ReplaceAllString(input, "")
|
return re.ReplaceAllString(input, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store a grid to an RLE file
|
||||||
|
func StoreGridToRLE(grid [][]int64, filename, rule string, width, height int) error {
|
||||||
|
fd, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pattern string
|
||||||
|
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
line := ""
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
switch grid[y][x] {
|
||||||
|
case 0:
|
||||||
|
line += "b"
|
||||||
|
case 1:
|
||||||
|
line += "o"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if first row is: 001011110, then line is now:
|
||||||
|
// bboboooob
|
||||||
|
|
||||||
|
encoded := RunLengthEncode(line)
|
||||||
|
|
||||||
|
// and now its: 2bob4ob
|
||||||
|
pattern += encoded
|
||||||
|
|
||||||
|
if y != height-1 {
|
||||||
|
pattern += "$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern += "!"
|
||||||
|
|
||||||
|
wrapped := ""
|
||||||
|
for idx, char := range pattern {
|
||||||
|
if idx%70 == 0 && idx != 0 {
|
||||||
|
wrapped += "\n"
|
||||||
|
}
|
||||||
|
wrapped += string(char)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(fd, "#N %s\nx = %d, y = %d, rule = %s\n%s\n",
|
||||||
|
filename, width, height, rule, wrapped)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// by peterSO on
|
||||||
|
// https://codereview.stackexchange.com/questions/238893/run-length-encoding-in-golang
|
||||||
|
func RunLengthEncode(s string) string {
|
||||||
|
e := make([]byte, 0, len(s))
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
j := i + 1
|
||||||
|
for ; j <= len(s); j++ {
|
||||||
|
if j < len(s) && s[j] == c {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if j-i > 1 {
|
||||||
|
e = strconv.AppendInt(e, int64(j-i), 10)
|
||||||
|
}
|
||||||
|
e = append(e, c)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = j - 1
|
||||||
|
}
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|||||||
3
rule.go
3
rule.go
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
// a GOL rule
|
// a GOL rule
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
|
Definition string
|
||||||
Birth []int64
|
Birth []int64
|
||||||
Death []int64
|
Death []int64
|
||||||
}
|
}
|
||||||
@@ -37,7 +38,7 @@ func ParseGameRule(rule string) *Rule {
|
|||||||
log.Fatalf("Invalid game rule <%s>", rule)
|
log.Fatalf("Invalid game rule <%s>", rule)
|
||||||
}
|
}
|
||||||
|
|
||||||
golrule := &Rule{}
|
golrule := &Rule{Definition: rule}
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
if part[0] == 'B' {
|
if part[0] == 'B' {
|
||||||
|
|||||||
2
sample-rles/glider.rle
Normal file
2
sample-rles/glider.rle
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
x = 3, y = 3, rule = B3/S23
|
||||||
|
3o$2bo$bo!
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
|
"github.com/tlinden/golsky/rle"
|
||||||
"golang.org/x/image/math/f64"
|
"golang.org/x/image/math/f64"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -310,6 +311,8 @@ func (scene *ScenePlay) CheckMarkInput() {
|
|||||||
scene.Markmode = false
|
scene.Markmode = false
|
||||||
scene.MarkTaken = false
|
scene.MarkTaken = false
|
||||||
scene.MarkDone = true
|
scene.MarkDone = true
|
||||||
|
|
||||||
|
scene.SaveRectRLE()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,6 +325,58 @@ func (scene *ScenePlay) SaveState() {
|
|||||||
log.Printf("saved game state to %s at generation %d\n", filename, scene.Generations)
|
log.Printf("saved game state to %s at generation %d\n", filename, scene.Generations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (scene *ScenePlay) SaveRectRLE() {
|
||||||
|
filename := GetFilenameRLE(scene.Generations)
|
||||||
|
|
||||||
|
if scene.Mark.X == scene.Point.X || scene.Mark.Y == scene.Point.Y {
|
||||||
|
log.Printf("can't save non-rectangle\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var width int
|
||||||
|
var height int
|
||||||
|
var startx int
|
||||||
|
var starty int
|
||||||
|
|
||||||
|
if scene.Mark.X < scene.Point.X {
|
||||||
|
// mark left point
|
||||||
|
startx = scene.Mark.X
|
||||||
|
width = scene.Point.X - scene.Mark.X
|
||||||
|
} else {
|
||||||
|
// mark right point
|
||||||
|
startx = scene.Point.X
|
||||||
|
width = scene.Mark.X - scene.Point.X
|
||||||
|
}
|
||||||
|
|
||||||
|
if scene.Mark.Y < scene.Point.Y {
|
||||||
|
// mark above point
|
||||||
|
starty = scene.Mark.Y
|
||||||
|
height = scene.Point.Y - scene.Mark.Y
|
||||||
|
} else {
|
||||||
|
// mark below point
|
||||||
|
starty = scene.Point.Y
|
||||||
|
height = scene.Mark.Y - scene.Point.Y
|
||||||
|
}
|
||||||
|
|
||||||
|
grid := make([][]int64, height)
|
||||||
|
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
grid[y] = make([]int64, width)
|
||||||
|
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
grid[y][x] = scene.Grids[scene.Index].Data[y+starty][x+startx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rle.StoreGridToRLE(grid, filename, scene.Config.Rule.Definition, width, height)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to save rect to %s: %s\n", filename, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("saved selected rect to %s at generation %d\n", filename, scene.Generations)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (scene *ScenePlay) Update() error {
|
func (scene *ScenePlay) Update() error {
|
||||||
scene.CheckInput()
|
scene.CheckInput()
|
||||||
scene.CheckDraggingInput()
|
scene.CheckDraggingInput()
|
||||||
|
|||||||
Reference in New Issue
Block a user