Here is with an "unsate" slice version that doesn't use experimental features.
It compiles but results are different. I added one string formatting but I
think another one is missing
#-------------------------------------------------------------------------------
# Name: WordSearch.nim
# Purpose: Find word in a grid of letters in any directions.
#-------------------------------------------------------------------------------
import std/[strutils, strformat, enumerate, sugar, tables, algorithm]
from math import floorMod,floorDiv
# Implement Python compatible divmod for nim-lang.
proc divmod(a, b: int): (int, int) =
let
res = a.floorDiv b # integer division
rem = a.floorMod b # integer modulo operation
(res, rem)
#-------------------------------------------------------------------------------
{.experimental: "views".}
type
View*[T] = object
## A strided view over an (unowned) data buffer
len*: int
stride: int
offset: int
data: ptr UncheckedArray[T] # "lent UncheckedArray[T]" requires devel
func `[]`*[T](v: View[T], idx: int): lent T {.inline.} =
v.data[v.offset + idx*v.stride]
func `[]`*[T](v: var View[T], idx: int): var T {.inline.} =
v.data[v.offset + idx*v.stride]
func `[]=`*[T](v: var View[T], idx: int, val: T) {.inline.} =
v.data[v.offset + idx*v.stride] = val
func toView*[T](oa: openArray[T]): View[T] {.inline.} =
result.len = oa.len
result.stride = 1
result.offset = 0
result.data = cast[ptr UncheckedArray[T]](oa[0].unsafeAddr)
iterator items*[T](v: View[T]): lent T =
var cur = v.offset
for _ in 0 ..< v.len:
yield v.data[cur]
cur += v.stride
func slice*(v: View, start, stop, step: int): View {.inline.} =
## Slice a view
## stop is inclusive
# General tensor slicing algorithm is
#
https://github.com/mratsim/Arraymancer/blob/71cf616/src/arraymancer/tensor/private/p_accessors_macros_read.nim#L26-L56
#
# for i, slice in slices:
# # Check if we start from the end
# let a = if slice.a_from_end: result.shape[i] - slice.a
# else: slice.a
#
# let b = if slice.b_from_end: result.shape[i] - slice.b
# else: slice.b
#
# # Compute offset:
# result.offset += a * result.strides[i]
# # Now change shape and strides
# result.strides[i] *= slice.step
# result.shape[i] = abs((b-a) div slice.step) + 1
#
# with slices being of size 1, as we have a monodimensional Tensor
# and the slice being a..<b with the reverse case: len-1 -> 0
#
# result is preinitialized with a copy of v (shape, stride, offset, data)
result.offset = v.offset + start * v.stride
result.stride = v.stride * step
result.len = abs((stop-start) div step) + 1
result.data = v.data
func `==`[T](a, b: View[T]): bool =
if a.len != b.len:
return false
var curA = a.offset
var curB = b.offset
for i in 0 ..< a.len:
if a.data[curA] != b.data[curB]:
return false
curA += a.stride
curB += b.stride
return true
#-------------------------------------------------------------------------------
# Procedure to print the grid of letters with row numbers & column letters.
proc print_grid(puzzle: string) =
var lastRow: string
let newGrid = puzzle.replace(" ", "")
for i, row in enumerate(1, newGrid.split('\n')):
# Print top column letters heading.
if i == 1:
stdout.write " "
for m in 0 ..< len(row):
stdout.write fmt"{chr(ord('a')+m)} "
echo()
stdout.write " +"
stdout.write "-".repeat(2 * len(row) + 1)
echo "+"
# Print row numbers heading.
stdout.write fmt"{i:02}| "
for m in row:
stdout.write fmt"{m} "
echo(fmt"|{i:02}")
# row is out of scope after the for-loop in Nim.
# Need a var lastRow to store the last value.
lastRow = row
# Print bottom column letters heading.
stdout.write " +"
stdout.write "-".repeat(2 * len(lastRow) + 1)
echo "+"
stdout.write " "
for m in 0 ..< len(lastRow):
stdout.write fmt"{chr(ord('a')+m)} "
echo()
# Algo1: Procedure to find word in grid of letters using Table.
proc solve_puzzle(puzzle: string; clues: seq) =
let
newGrid = puzzle.replace(" ", "")
length = newGrid.find('\n') + 1
# Make a list of tuples containing each letter and its row and column.
letters = collect(newSeq):
for index, letter in enumerate(newGrid): (letter, divmod(index,
length))
# Reorder the list to represent each reading direction,
# and add them all to a dictionary.
var lines = initTable[string, typeof(letters)]()
let offsets = {"down": 0, "left down": -1, "right down": 1}.toTable
for direction, offset in offsets:
lines[direction] = @[] # Empty seq.
for i in 0 ..< length:
for j in countup(i, letters.high, length + offset):
lines[direction].add(letters[j])
lines[direction].add(('\n', (0, 0)))
lines["right"] = letters
lines["left"] = letters.reversed
lines["up"] = lines["down"].reversed
lines["left up"] = lines["right down"].reversed
lines["right up"] = lines["left down"].reversed
# Make strings from the letters, find the words in them and retrieve
# their original locations.
var nc = 0
for direction, tup in lines:
let myStr = block:
var x = ""
for line in tup: x &= line[0]
x
for word in clues:
if word.toUpper() in myStr:
nc = nc + 1
var location = tup[myStr.find(word.toUpper())][1]
echo fmt"{word.toUpper()}: row[{location[0] + 1:02}]
col[{chr(ord('a') + location[1])}] {direction}"
if (nc mod 5) == 0 and nc < clues.len:
echo "-".repeat 35
# Algo2: Function to find word in grid of letters using Array.
proc find_word(puzzle: string; word: string) =
echo(fmt"{word}:")
let newGrid = puzzle.replace(" ","")
let w = newGrid.find("\n")+1
let L = word.len
let viewGrid = newGrid.toView() # nim(165, 25) template/generic
instantiation of `toView` from here.
# nim(43, 17) Error: expression cannot
be cast to lent UncheckedArray[char]
var res: seq[(int, int)]
for i in 0 ..< len(newGrid):
for j in @[-w-1, -w, -w+1, -1, 1, w-1, w, w+1]:
for x in i ..< i+(L-1)*j:
if viewGrid.slice(i, viewGrid.len-1,j).slice(0, L-1, 1) ==
word.toUpper().toView(): # Not sure if this is correct.
res.add((x div (w + 1), (x mod w) + 1))
for t in res:
echo " --> ".join(&"Row[t[0]:02] Col[{chr(ord('a') + t[1] - 1)}]")
#===============================================================================
when is_main_module:
# Define the puzzle.
let puzzle = """I U W S S W E D E N F Q S M V R P B
B M I D F I N D I A H I S P W Y Y L
H T E N I G E R I A I O N V A M C P
Y Y H X R L E B E T G R J L O I A A
H N X A I U E Z F R A N C E A E N N
V R O T I C S N B E L G I U M N A A
N F R R Y L O S G I C V M N V E D M
W N X F W V A I I L T O Z I H A A A
G H A N A A M N X A A A F B K I N B
G R E E C E Y A D A D N L Z U D F Z
T P K P Q J A P A N T Q D Y D L V I
V K A M E R I C A D C H I N A B C X"""
let clues = @["Belgium", "Greece", "Canada", "Japan", "England",
"Italy", "France", "Panama", "India", "Mexico",
"Norway", "Spain", "China", "Thailand", "Sweden",
"Ghana", "Russia", "Finland", "Nigeria", "America"]
# Print the grid.
print_grid(puzzle)
# Find the words.
echo()
echo "=".repeat 40
solve_puzzle(puzzle, clues)
echo "=".repeat 40
# Another algo to find words.
echo()
echo "=".repeat 40
var nw = 0
for word in clues:
nw = nw + 1
find_word(puzzle, word)
if nw mod 5 == 0 and nw < len(clues):
echo "-".repeat 35
else:
if nw != len(clues):
echo()
echo "=".repeat 40
echo()
Run