-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrle.lua
More file actions
172 lines (132 loc) · 4.16 KB
/
Copy pathrle.lua
File metadata and controls
172 lines (132 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
--[[
rle.lua
Handles all logic for the Run-Length Encoded (RLE) pattern format.
requires emptyGrid() from utils
Contains:
- save: Encodes the current grid pattern to the clipboard
- load: Loads an RLE pattern from a string onto the grid
- parseRLE: Parses a raw RLE string into a pattern table
- rle_encode: RLE compression algorithm
- rle_decode: RLE decompression algorithm
]]
local utils = require('utils')
-- @data: Square table contianing 0/1
-- RL-encodes pattern data and saves it in clipboard
-- https://conwaylife.com/wiki/Run_Length_Encoded
function save(data)
local text = ''
local size = #data
for j = 1, size do
local line = ''
for i = 1, size do
line = line..((data[i][j] == 0) and 'b' or 'o')
end
line = rle_encode(line)
text = text..line..'$' -- Tag representing end of line
end
love.system.setClipboardText( text )
end
-- @size : Integer, size of grid on which pattern will be placed
-- @pattern: String containing pattern
-- returns data table if successful or nil if not
function load(size, pattern)
-- Remove whitespace, newline
pattern = pattern:gsub('\n', '')
pattern = pattern:gsub('%s', '')
-- Check if decoded text has only valid chars
if string.match(pattern, '[^ob0-9$!]+') then
return nil
end
-- Decode pattern, and limit it's size to grid dimensions
local patternTable, patternHeight, patternWidth = parseRLE(pattern, size)
-- Find coords to place pattern in center of grid
local startX = size/2 - math.floor(patternWidth/2)
local startY = size/2 - math.floor(patternHeight/2)
local data = utils.emptyGrid(size)
for i = 1, #patternTable do
for j = 1, #patternTable[i] do
local dx = startY + i
local dy = startX + j
data[dy][dx] = (patternTable[i]:sub(j,j)) == 'o' and 1 or 0
end
end
return data
end
-- @text : RLE string
-- Returns a table, patternHeight and patternWidth
function parseRLE(text, size)
-- Explicitly add 1 before instances of single letters
-- 2bo -> bobo, 2b1o -> bbo
text = text:gsub('([ob])([ob])','%11%2')
text = text:gsub('^([ob])','1%1')
text = text:gsub('$([ob])','$1%1')
-- Add ! to end of string if not there
text = text:gsub('([ob])$','%1!')
-- Decode patterns like 2$ so that new pattern lines are handled correctly
text = text:gsub('[%d]+%$',rle_decode)
-- Loop through every character, decode and store pattern lines
local lines = {}
local line = ''
local patternWidth = 0
for char in text:gmatch"." do
-- Handling end of pattern line
if (char == "$" or char == "!") then
-- run-length-decode line
line = rle_decode(line)
-- Limit pattern line to size
line = line:sub(1,size)
-- Update pattern max width
patternWidth = math.max(patternWidth, #line)
-- Insert line into list of lines
table.insert(lines, line)
-- Reset line
line = ''
-- Stop loop if pattern has ended or
-- height of pattern is same as grid height oo$obo
if #lines == size or char == "!" then
break
end
else -- If char is not end of line, simply add to line buffer
line = line..char
end
end -- End of looping through every character
local patternHeight = #lines
return lines, patternHeight, patternWidth
end
-- https://github.com/kennyledet/Algorithm-Implementations/blob/master/Run_Length_Encoding/Lua/Yonaba/rle.lua
-- Run-Length Encoding Compression algorithm implementation
-- Compresses an input string using RLE algorithm
-- @str : an input string to be compressed
-- returns : the encoded string
function rle_encode(str)
local prev = str:sub(1,1)
local count = 0
local encoded = ''
for char in str:gmatch('.') do
if char == prev then
count = count + 1
else
encoded = encoded .. (count .. prev)
prev = char
count = 1
end
end
return encoded .. (count .. prev)
end
-- Decodes a given input
-- @str : an encoded string
-- returns : the original string
function rle_decode(str)
local decoded_str = ''
for count, match in str:gmatch('(%d+)([^%d]+)') do
decoded_str = decoded_str .. (match:rep(count))
end
return decoded_str
end
return {
save = save,
load = load,
parseRLE = parseRLE,
rle_encode = rle_encode,
rle_decode = rle_decode
}