python 3d A* pathfinging infinite loop - python-3.x

I am trying to adapt an application I found here, I imagined that I just had to add an axis. The problem is that the script seems like it is stuck. Can someone tell me what I did wrong and how I can use A* with a 3d matrix (i j k)?
this is the portion of the A* function I changed
for new_position in [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]: # Adjacent squares, (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent squares removed to ignor diagonal movement
# Get node position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1], current_node.position[2] + new_position[2])
# Make sure within range
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0 or node_position[2] > (len(maze) - 1) or node_position[2] < 0 or node_position[2] > (len(maze[len(maze)-1]) -1):
continue
# Make sure walkable terrain
if maze[node_position[0]][node_position[1]][node_position[2]] != 0:
continue
it was originally:
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent squares
# Get node position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
# Make sure within range
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0:
continue
# Make sure walkable terrain
if maze[node_position[0]][node_position[1]] != 0:
continue
this is the whole script with my alterations:
import numpy as np
class Node():
"""A node class for A* Pathfinding"""
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
def astar(maze, start, end):
"""Returns a list of tuples as a path from the given start to the given end in the given maze"""
# Create start and end node
start_node = Node(None, start)
start_node.g = start_node.h = start_node.f = 0
end_node = Node(None, end)
end_node.g = end_node.h = end_node.f = 0
# Initialize both open and closed list
open_list = []
closed_list = []
# Add the start node
open_list.append(start_node)
# Loop until you find the end
while len(open_list) > 0:
# Get the current node
current_node = open_list[0]
current_index = 0
for index, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = index
# Pop current off open list, add to closed list
open_list.pop(current_index)
closed_list.append(current_node)
# Found the goal
if current_node == end_node:
path = []
current = current_node
while current is not None:
path.append(current.position)
current = current.parent
return path[::-1] # Return reversed path
# Generate children
children = []
for new_position in [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]: # Adjacent squares, (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent squares removed to ignor diagonal movement
# Get node position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1], current_node.position[2] + new_position[2])
# Make sure within range
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0 or node_position[2] > (len(maze) - 1) or node_position[2] < 0 or node_position[2] > (len(maze[len(maze)-1]) -1):
continue
# Make sure walkable terrain
if maze[node_position[0]][node_position[1]][node_position[2]] != 0:
continue
# Create new node
new_node = Node(current_node, node_position)
# Append
children.append(new_node)
# Loop through children
for child in children:
# Child is on the closed list
for closed_child in closed_list:
if child == closed_child:
continue
# Create the f, g, and h values
child.g = current_node.g + 1
child.h = ((child.position[0] - end_node.position[0]) ** 2) + ((child.position[1] - end_node.position[1]) ** 2)+((child.position[2] - end_node.position[2]) ** 2)
child.f = child.g + child.h
# Child is already in the open list
for open_node in open_list:
if child == open_node and child.g > open_node.g:
continue
# Add the child to the open list
open_list.append(child)
def main():
maze = np.zeros((12,12,12))
start = (10, 9, 9)
end = (1, 1, 1)
path = astar(maze, start, end)
print(path)
if __name__ == '__main__':
main()

A* can work with any number of dimensions; it's a graph traversal algorithm, and no matter how many dimensions your problem space has, connecting one position to another still produces a graph.
You have two problems with generating new nodes, however.
You included (0, 0, 0) in the list, so no change. You keep putting the current position back into the queue for consideration. That’s just busy work, because the current position is already in the closed list.
You never subtract from any of your coordinates, you only add. So your x, y and z values can only ever go up. If reaching your goal requires a path around an obstacle, then you have a problem here because all your version can ever do is move in one direction along any given axis.
In a 3 by 3 by 3 3D matrix, with the current position in the middle, there are 3 times 3 times 3 minus 1 == 26 positions you can reach with a single step. Your code reaches just 7 of those, plus one that stays put.
If you extract your tuples in the for new_position in [...] list into a separate variable and add some newlines, and re-arrange them a bit to group them by how many 1s you have in the tuple, you get the following definition. I renamed this to deltas, because it's not a new position, it's the change relative to the old position, or delta. I re-arranged your tuples to make it easier to group them logically:
deltas = [
(0, 0, 0), # no change,
(0, 0, 1), (0, 1, 0), (1, 0, 0), # add to a single axis
(0, 1, 1), (1, 0, 1), (1, 1, 0), # add to two axes
(1, 1, 1) # move in all 3 directions at once.
]
for delta in deltas:
# ...
You want to drop the first one ((0, 0, 0)), and you need to add -1 versions of the other variants. You need 26 different tuples, but writing those by hand gets to be cumbersome, really fast. You can use itertools.product() to generate these, instead:
from itertools import product
# combinations of -1, 0 and 1, filtering out the (0, 0, 0) case:
deltas = [d for d in product((-1, 0, 1), repeat=3) if any(d)]
Now, your comment next to the loop says:
# Adjacent squares removed to ignor diagonal movement
It's not entirely clear what you mean by this, because your original list of deltas includes diagonals ((0, 1, 1) moves both in the y and z directions, and you have tuples combining movement in the x plus y and the x plus z axes), and even one that moves diagonal by incrementing all 3 axes. If you only wanted to move up, down, left, right, forward, and backward, you want to restrict movement to a single axis at a time, and deltas should be:
# movement without diagonals
deltas = [
(1, 0, 0), (-1, 0, 0), # only x
(0, 1, 0), (0, -1, 0), # only y
(0, 0, 1), (0, 0, -1), # only z
]
Personally, I'd move the whole business of generating new positions to a separate method on the Node class:
def possible_moves(self, map):
x, y, z = self.x, self.y, self.z
for dx, dy, dz in DELTAS:
newx, newy, newz = x + dx, y + dy, z + dz
try:
if maze[newx][newy][newz] != 0:
yield Node(self, (newx, newy, newz))
except IndexError:
# not inside the maze anymore, ignore
pass
This method assumes that there is a DELTAS global variable that defines the possible moves, and is a generator; each time yield is reached a new Node() instance is returned to whatever is using this as an iterator (like a for loop would).
Then just use that method instead of the children = [] list you filled with the for new_position in ...: loop, so directly in the part that uses the original children list:
# Loop through children
for child in current_node.possible_moves(map):
# Child is on the closed list
for closed_child in closed_list:
if child == closed_child:
continue
# etc.

Related

Delay in drawing an element with mouse click if user input is requested in open cv python

I wanted to draw points with labels at mouse click on an image. In the following code it draws blue points for first 3 left mouse clicks, thereafter red points. It works fine so far. However, if I enable the user input requesting part marked by <--- it doesn't show the 3rd blue point until I give the user input. What I was expecting was the user input request comes after it draws the 3rd blue point. Any help is appreciated. Thanks !!
Here's the code. Please comment out <--- marked line #26 which calls for the user input and it works fine.
import cv2
img_path = r'test.jpg'
count = 0
red_count = 0
user_inp = None
def click_event(event, x, y, flags, params):
global count, red_count, user_inp
font_scale, point_radius = 0.4, 2
font = cv2.FONT_HERSHEY_SIMPLEX
if event == cv2.EVENT_LBUTTONDOWN:
if count < 3:
text = f"blue{count}"
cv2.putText(img, text, (x + 2, y), font, font_scale, (255, 0, 0), 1)
cv2.circle(img, (x, y), radius=point_radius, color=(255, 0, 0), thickness=-1)
cv2.imshow('image', img)
if count == 2:
print("blue index 2")
user_inp = input('input anything: ') # <------------ check here
if count >= 3:
text = f"red{red_count}"
cv2.putText(img, text, (x + 2, y), font, font_scale, (0, 0, 255), 1)
cv2.circle(img, (x, y), radius=point_radius, color=(0, 0, 255), thickness=-1)
cv2.imshow('image', img)
red_count += 1
count += 1
if __name__ == "__main__":
img = cv2.imread(img_path, 1)
cv2.imshow('image', img)
cv2.setMouseCallback('image', click_event)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(user_inp)

Text Justification / Word Wrap dp problem.Unable to find the overlapping condition in my recursion?

Que: https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap
What I tried & issue faced: I tried a recursive approach to solve this problem. Currently, unable to find the overlapping subproblems. Can anyone help me how can I optimize, memoize and modify/update my recursion approach?. (I am thinking my approach is Wrong)
approach: Either the word is in the current line(if and only if space is left) or will be in the new line.
Code Flow:
Input text = "Cat is on Floor" .
I stored len in an array = [3,2,2,5]
ans(a,b,c) : a: index, b: line (currently no use), c: current line sum
base condn if all elements are over a>=size: return pow(spaceleft,2)
will return min( subproblem(word in current line),subproblem(word in new line)+pow(spaceleft,2))
Initially : ans(1,1,3)
ans(2,1,6) ans(2,2,2)
ans(3,2,2) ans(3,2,5) ans(3,3,2)
ans(4,3,5) ans(4,3,5) ans(4,4,5)
The below code is in python3.x :
n=6
arr=[3,2,2,5]
size = 4
def ans(a,b,c):
if(a>=size):
return pow((n-c),2);
if(c+arr[a]+1 > n):
return (pow((n-c),2)+ans(a+1,b+1,arr[a]))
return (min(ans(a+1,b,c+arr[a]+1),pow((n-c),2)+ans(a+1,b+1,arr[a])))
print(ans(1,1,3))
Thanks in advance for giving your valuable time and helping me....
I think your formulation might be missing some cases. It surely is hard to understand. Here's one that seems to get the right answer.
class LineWrapper:
def __init__(self, lens, width):
self.lens = lens
self.width = width;
def cost(self, ptr=0, used=0):
remaining = self.width - used
# Case 1: No words: Cost is due to partially used line.
if ptr == len(self.lens):
return remaining ** 2 if used else 0
# Case 2: First word of line. Must skip.
if used == 0:
return self.cost(ptr + 1, self.lens[ptr])
# Case 3: Out of space. Must wrap.
needed = 1 + self.lens[ptr]
if remaining < needed:
return remaining ** 2 + self.cost(ptr)
# Case 4: Min cost of skip and wrap.
return min(self.cost(ptr + 1, used + needed), remaining ** 2 + self.cost(ptr))
There's lots of overlap among subproblems in this formulation, and yours, too. A simple example is [1, 1, 1, 1] with a width of 7. The solution will try putting this on all combinations of 1, 2, 3, and 4 lines. May sub-combinations will repeat.
To see this more obviously, we can memoize and check for hits:
def memo_cost(self, ptr=0, used=0):
args = (ptr, used)
print(args)
if args in self.memos:
print(f'Memo hit: {args}')
return self.memos[args]
remaining = self.width - used
# Case 1: No words has cost of partially used line
if ptr == len(self.lens):
r = remaining ** 2 if used else 0
self.memos[args] = r
print(f'Done: {r}')
return r
# Case 2: First word of line. Must skip.
if used == 0:
r = self.memo_cost(ptr + 1, self.lens[ptr])
self.memos[args] = r
print(f'Must skip: {r}')
return r
# Case 3: Out of space. Must wrap.
needed = 1 + self.lens[ptr]
if remaining < needed:
r = remaining ** 2 + self.memo_cost(ptr)
self.memos[args] = r
print(f'Must wrap: {r}')
return r
# Case 4: Min cost of skip wrap and wrap.
r = min(remaining ** 2 + self.memo_cost(ptr), self.memo_cost(ptr + 1, used + needed))
self.memos[args] = r
print(f'Min: {r}')
return r
print(LineWrapper([1, 1, 1, 1], 7).memo_cost())
When run, this produces:
$ python3 lb.py
(0, 0)
(1, 1)
(1, 0)
(2, 1)
(2, 0)
(3, 1)
(3, 0)
(4, 1)
Done: 36
Must skip: 36
(4, 3)
Done: 16
Min: 16
Must skip: 16
(3, 3)
(3, 0)
Memo hit: (3, 0)
(4, 5)
Done: 4
Min: 4
Min: 4
Must skip: 4
(2, 3)
(2, 0)
Memo hit: (2, 0)
(3, 5)
(3, 0)
Memo hit: (3, 0)
(4, 7)
Done: 0
Min: 0
Min: 0
Min: 0
Must skip: 0
0
My answer with memo thanks to #Gene
n=7
arr=[3,2,2,5]
INF = 9223372036854775807
size = 4
dp = [[INF for i in range(n+1)] for j in range(size+1)]
def ans(a,b,c):
if(dp[a][c]!=INF):
return dp[a][c]
if(a>=size):
dp[a][c] = pow((n-c),2)
return pow((n-c),2)
if(c+arr[a]+1 > n):
dp[a][c] = (pow((n-c),2)+ans(a+1,b+1,arr[a]))
return dp[a][c]
dp[a][c] = (min(ans(a+1,b,c+arr[a]+1),pow((n-c),2)+ans(a+1,b+1,arr[a])))
return dp[a][c]
print(ans(1,1,3))

Move Robot in a 10 X 10 grid

I am working on this code challenge:
Given a 2D bot/robot which can only move in four directions, move forward which is UP(U), move backward which is DOWN(D), LEFT(L), RIGHT(R) in a 10x10 grid. The robot can't go beyond the 10x10 area.
Given a string consisting of instructions to move.
Output the coordinates of a robot after executing the instructions. Initial position of robot is at origin(0, 0).
Example:
Input : move = “UDDLRL”
Output : (-1, -1)
Explanation:
Move U : (0, 0)–(0, 1)
Move D : (0, 1)–(0, 0)
Move D : (0, 0)–(0, -1)
Move L : (0, -1)–(-1, -1)
Move R : (-1, -1)–(0, -1)
Move L : (0, -1)–(-1, -1)
Therefore final position after the complete
movement is: (-1, -1)
I got the code working without using the 10x10 grid information. How could I incorporate the 10x10 grid information into my solution in an OOP fashion? My solution doesn't follow the OOP principles.
# function to find final position of
# robot after the complete movement
def finalPosition(move):
l = len(move)
countUp, countDown = 0, 0
countLeft, countRight = 0, 0
# traverse the instruction string 'move'
for i in range(l):
# for each movement increment its respective counter
if (move[i] == 'U'):
countUp += 1
elif(move[i] == 'D'):
countDown += 1
elif(move[i] == 'L'):
countLeft += 1
elif(move[i] == 'R'):
countRight += 1
# required final position of robot
print("Final Position: (", (countRight - countLeft),
", ", (countUp - countDown), ")")
# Driver code
if __name__ == '__main__':
move = "UDDLLRUUUDUURUDDUULLDRRRR"
finalPosition(move)
This fixes it:
class Robot:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
new_x = x + self.x
new_y = y + self.y
if (new_x > 9 or new_y > 9):
raise ValueError("Box dimensions are greater than 10 X 10")
return new_x, new_y
WALKS = dict(U=Mover(0, 1), D=Mover(0, -1),
L=Mover(-1, 0), R=Mover(1, 0))
def move(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return (x,y)
if __name__ == '__main__':
moves2 = "UDDLLRUUUDUURUDDUULLDRRRR"
robot = Robot()
print(robot.move(moves2))
Output :
(2,3)
The way you use your counters makes it less trivial to detect that you would hit the border of the 10x10 grid. Without changing too much, you could replace the countUp and countDown variables by one countVertical variable, and add -1 to it when going up and 1 when going down. Then ignore a move if it would make that counter negative or greater than 9. And obviously you would do the same for horizontal movements.
[Edit: After the edit to your question, it turns out that you want the Y-coordinate to be opposite to what I assumed above. So I have changed the sign of the Y-coordinate updates (+1, -1).]
That's really it.
Now to make this more OOP, you could define a Robot class, which would maintain its x and y coordinate. Anyhow it would be good to remove the print call out of your function, so the function only deals with the movements, not with the reporting (separation of concern).
Here is how it could work:
class Robot:
def __init__(self, x=0, y=0):
self.position(x, y)
def position(self, x, y):
self.x = min(9, max(0, x))
self.y = min(9, max(0, y))
def move(self, moves):
for move in moves:
if move == 'U':
self.position(self.x, self.y + 1)
elif move == 'D':
self.position(self.x, self.y - 1)
elif move == 'L':
self.position(self.x - 1, self.y)
elif move == 'R':
self.position(self.x + 1, self.y)
else:
raise ValueError(f"Invalid direction '{move}'")
if __name__ == '__main__':
moves = "UDDLLRUUUDUURUDDUULLDRRRR"
robot = Robot(0, 0)
robot.move(moves)
print(f"Final position: {robot.x}, {robot.y}")

How do I implement a custom shader in PyOpenGL?

I am diving into 3d graphics with PyOpenGL and am thoroughly overwhelmed. I want to be able to implement shadows in my scenes and prevent GL from drawing object through each other. This is the way I see it:
The best way I can see is a custom shader.
According to the PyOpenGL tutorials on SourceForge, I need a whole bunch of libraries. I want to do this with just PyOpenGL if possible.
I need this shader to be PyQt5 compatible.
As much as possible, I would like to render in the GPU. I believe that this would permit the CPU to focus on my data handling, etc. in my project to fully utilize all resources.
If I make a custom shader, I would like to include a basic function to draw lines and faces. Something like I do normally, like glvertex3f.
How do I go about this? I looked at this tutorial, but I cannot sort out what is bare necessity and what is not.
This is what I am using right now, but GL will draw shapes on top of each other. It also has no sort of shadow rendering.
# File structure is as follows:
# imports
# exceptions
# shape classes
# main shader
#---------- imports ----------#
from OpenGL.GL import (
glLoadIdentity, glTranslatef, glRotatef,
glClear, glBegin, glEnd,
glColor3fv, glVertex3fv,
GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_QUADS, GL_LINES
)
from OpenGL.GLU import gluPerspective
#---------- exceptions ----------#
class shapeNotFound(Exception):
pass
#---------- shape classes ----------#
class cube():
render = True
def __init__(self, location = (0, 0, 0), size = 0.1, drawWires = True, drawFaces = False, color = (1, 1, 1)):
self.location = location
self.size = size
self.drawWires = drawWires
self.drawFaces = drawFaces
self.color = color
self.compute()
def compute(self):
x, y, z = self.location
s = self.size / 2
self.vertices = [ #8 corner points calculated in reference to the supplied center point
(-s + x, s + y, -s + z), (s + x, s + y, -s + z),
(s + x, -s + y, -s + z), (-s + x, -s + y, -s + z),
(-s + x, s + y, s + z), (s + x, s + y, s + z),
(s + x, -s + y, s + z), (-s + x, -s + y, s + z)
]
self.wires = [ #12 tuples referencing the corner points
(0,1), (0,3), (0,4), (2,1), (2,3), (2,6),
(7,3), (7,4), (7,6), (5,1), (5,4), (5,6)
]
self.facets = [ #6 tuples referencing the corner points
(0, 1, 2, 3), (0, 1, 6, 5), (0, 3, 7, 4),
(6, 5, 1, 2), (6, 7, 4, 5), (6, 7, 3, 2)
]
def show(self):
self.render = True
def hide(self):
self.render = False
def move(self, location):
self.location = location
self.compute()
def recolor(self, col):
if type(col) is tuple:
self.color = col
class mesh():
vertices = []
facets = []
wires = []
render = True
def __init__(self, drawWires = True, drawFaces = False, color = (1, 1, 1)):
self.drawWires = drawWires
self.drawFaces = drawFaces
self.color = color
self.vertices = []
self.facets = []
self.wires = []
self.render = True
def addFacet(self, coords): #takes a tuple of three location tuples.
addr = len(self.vertices)
addrs = [None, None, None]
for i in range(3):
c = coords[i]
if not c in self.vertices:
self.vertices.append(c)
addrs[i] = self.vertices.index(c)
self.facets.append((addrs[0], addrs[1], addrs[2]))
self.wires.append((addrs[0], addrs[1]))
self.wires.append((addrs[2], addrs[1]))
self.wires.append((addrs[2], addrs[0]))
#---------- main shader ----------#
class shader():
#variables
parent = None
shapes = []
shapeTypes = [type(cube), type(mesh)]
#functions
def __init__(self, parent = None):
self.parent = parent
print('Initiated new shader as child of {}.'.format(self.parent))
def resize(self, newSize):
self.sizeX, self.sizeY = newSize
def addShape(self, shapeIn):
if type(shapeIn) not in self.shapeTypes:
raise shapeNotFound("Shape {} not found.".format(shapeIn))
self.shapes.append(shapeIn)
def paintGL(self):
#This function uses shape objects, such as cube() or mesh(). Shape objects require the following:
#a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
#a list named 'wires' - This list is a list of tuples which refer to vertices, dictating where to draw wires.
#a list named 'facets' - This list is a list of tuples which refer to vertices, ditating where to draw facets.
#a bool named 'render' - This bool is used to dictate whether or not to draw the shape.
#a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
#a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.
glLoadIdentity()
gluPerspective(45, self.sizeX / self.sizeY, 0.1, 110.0) #set perspective?
glTranslatef(0, 0, self.zoomLevel) #I used -10 instead of -2 in the PyGame version.
glRotatef(self.rotateDegreeV, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glRotatef(self.rotateDegreeH, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
if len(self.shapes) != 0:
glBegin(GL_LINES)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.drawWires:
for w in s.wires:
for v in w:
glVertex3fv(s.vertices[v])
glEnd()
glBegin(GL_QUADS)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.drawFaces:
for f in s.facets:
for v in f:
glVertex3fv(s.vertices[v])
glEnd()

Random walk simulation in python 3.6

I have been trying to simulate a random walk using the code below
import random
def random_walk(n):
""" Return coordiantes after 'n' block random walk"""
x, y = 0, 0
# y = 0
for i in range(n):
(dx, dy) = random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
x = x+dx
y = y+dy
return(x, y)
for i in range(25):
walk = random_walk(10)
print(walk, "Distance from origin:",
abs(walk[0]) + abs(walk[1]))
I am always getting output as 1.No matter how much i increase number of walks or
number of blocks walked.I am not able to figure out what am i doing wrong
Just an indenting problem in the first for loop.
import random
def random_walk(n):
""" Return coordiantes after 'n' block random walk"""
x, y = 0, 0
# y = 0
for i in range(n):
(dx, dy) = random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
x = x+dx
y = y+dy
return(x, y)
for i in range(25):
walk = random_walk(10)
print(walk, "Distance from origin:", abs(walk[0]) + abs(walk[1]))

Resources