Archived
1
0
Fork 0
This repository has been archived on 2025-03-10. You can view files and clone it, but cannot push or open issues or pull requests.
nimfunge98/src/field.nim
2021-10-17 21:45:28 +02:00

218 lines
5.9 KiB
Nim

type
Line = object
x: int
columns: seq[int]
Field* = object
x, y: int
lx: int
lines: seq[Line]
func Blank*(f: var Field, x, y: int) =
let ly = f.lines.len
if y < f.y or y >= f.y+ly: # outside the field
return
var l = addr f.lines[y-f.y]
let ll = l.columns.len
if x < l.x or x >= l.x+ll: # outside the field
return
if x > l.x and x < l.x+ll-1: # just set the value
l.columns[x-l.x] = int(' ')
return
if ll == 1: # this was the last character on the line
if y == f.y: # we need to trim the leading lines
var i = 1
while f.lines[i].columns.len == 0:
inc i
f.y += i
f.lines = f.lines[i..<ly]
elif y == f.y+ly-1: # we need to trim the trailing lines
var i = ly-2
while f.lines[i].columns.len == 0:
dec i
f.lines = f.lines[0..i]
else: # it was a line in the middle
l.columns = @[]
elif x == l.x: # we need to remove leading spaces
var i = 1
while l.columns[i] == int(' '):
inc i
l.x += i
l.columns = l.columns[i..<ll]
elif x == ll+l.x-1: # we need to remove trailing spaces
var i = ll-2
while l.columns[i] == int(' '):
dec i
l.columns = l.columns[0..i]
# we now need to calculate the new field limits
f.x = f.lines[0].x
var x2 = f.lines[0].columns.len + f.lines[0].x
for i in 1..<f.lines.len:
if f.lines[i].columns.len == 0:
continue
if f.x > f.lines[i].x:
f.x = f.lines[i].x
if x2 < f.lines[i].x + f.lines[i].columns.len:
x2 = f.lines[i].x + f.lines[i].columns.len
f.lx = x2-f.x
func Get*(f: Field, x, y: int): int =
if y >= f.y and y < f.y+f.lines.len:
let l = f.lines[y-f.y]
if x >= l.x and x < l.x+l.columns.len:
return l.columns[x-l.x]
return int(' ')
func IsIn*(f: Field, x, y: int): bool =
return x >= f.x and y >= f.y and x < f.x+f.lx and y < f.y+f.lines.len
proc Load*(filename: string): ref Field =
var file: File
if not open(file, filename):
return nil
defer: file.close()
var f: ref Field
new(f)
var l : ptr Line
var leadingSpaces = 0
var trailingSpaces = 0
var data: array[4096, char]
var lastReadIsCR = false
while true:
let n = file.readChars(data, 0, 4096)
if n <= 0:
if f.lines.len == 0:
if l == nil: # we got en empty file!
return nil
f.x = l.x
if l != nil:
if f.lx < l.columns.len+l.x-f.x:
f.lx = l.columns.len+l.x-f.x
break
var i = 0
while i < n:
if data[i] == char(12):
inc i
continue
if lastReadIsCR and data[i] == '\n':
inc i
lastReadIsCR = false
continue
if data[i] == '\n' or data[i] == '\r':
if f.lines.len == 0:
return nil
if f.lines.len == 1:
f.x = l.x
if l != nil:
if f.x > l.x:
f.x = l.x
if f.lx < l.columns.len+l.x-f.x:
f.lx = l.columns.len+l.x-f.x
else:
f.lines.add(Line())
l = nil
trailingSpaces = 0
if data[i] == '\r':
if i+1 < n and data[i+1] == '\n':
inc i
else:
lastReadIsCR = true
else:
if data[i] == ' ':
if l == nil: # trim leading spaces
inc leadingSpaces
else:
inc trailingSpaces
else:
if l == nil:
f.lines.add(Line())
l = addr f.lines[^1]
l.x = leadingSpaces
leadingSpaces = 0
if trailingSpaces > 0:
let oldL = l.columns.len
l.columns.setlen(oldL+trailingSpaces+1)
for j in oldL..<l.columns.len-1:
l.columns[j] = int(' ')
l.columns[^1] = int(data[i])
trailingSpaces = 0
else:
l.columns.add(int(data[i]))
inc i
return f
func Set*(f: var Field, x, y, v: int) =
if v == int(' '):
f.Blank(x, y)
elif y >= f.y:
if y < f.y+f.lines.len: # the line exists
var l = addr f.lines[y-f.y]
if l.columns.len == 0: # An empty line is a special case
l.x = x
l.columns = @[v]
if f.x > x:
f.lx = f.lx+f.x-x
f.x = x
if f.lx < x-f.x+1:
f.lx = x-f.x+1
elif x >= l.x:
if x < l.x+l.columns.len: # just set the value
l.columns[x-l.x] = v
else: # append columns
let oldL = l.columns.len
l.columns.setlen(x-l.x+1)
for i in oldL..<l.columns.len-1:
l.columns[i] = int(' ')
l.columns[^1] = v
if f.lx < l.columns.len+l.x-f.x:
f.lx = l.columns.len+l.x-f.x
else: # preprend columns
var newline = newSeqUninitialized[int](l.columns.len+l.x-x)
newline[0] = v
for i in 1..<l.x-x:
newline[i] = int(' ')
for i in l.x-x..<newline.len:
newline[i] = l.columns[i-l.x+x]
l.columns = newline
l.x = x
if f.x > x:
f.lx = f.lx + f.x - x
f.x = x
else: # append lines
f.lines.setlen(y-f.y+1)
f.lines[y-f.y] = Line(x: x, columns: @[v])
if f.x > x:
f.lx = f.lx + f.x - x
f.x = x
if f.lx < x-f.x+1:
f.lx = x-f.x+1
else: # prepend lines
var newlines = newSeq[Line](f.lines.len+f.y-y)
newlines[0] = Line(x: x, columns: @[v])
for i in f.y-y..<newlines.len:
newlines[i] = f.lines[i-f.y+y]
f.lines = newlines
f.y = y
if f.x > x:
f.lx = f.lx+f.x-x
f.x = x
if f.lx < x-f.x+1:
f.lx = x-f.x+1
func Step*(f: Field, v: tuple[x, y: int], d: tuple[x, y: int]): (int, int) =
var x = v.x + d.x
var y = v.y + d.y
if f.IsIn(x, y):
return (x, y)
# We are stepping outside, we need to wrap the Lahey-space
x = v.x
y = v.y
while true:
let x2 = x - d.x
let y2 = y - d.y
if not f.IsIn(x2, y2):
return (x, y)
x = x2; y = y2
func GetSize*(f: Field): (int, int, int, int) =
return (f.x, f.y, f.lx, f.lines.len)