Python nested loop to identify characters in string of text - python

I have a string of text that reads:
1x2xx1x2xx1x2xx1x2xxx
I need to take apart the string of text, and if it is a number, i want to pass that number along with a few other variables to another function to print a square on a canvas.
I wrote the following code:
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile (a_canvas, type_of_tile, x, y, size)
else:
x += size
The issue I am having is:
It doesn't seem to work, the rectangles and shapes that draw_tile
is supposed to print don't appear (but the draw_tile function
works fine, because it is being referenced multiple times else where
in the program where it prints just perfect)
At the end of the loop I want to increase the y value by y+=size as
well, so that when the NEXT string of text is passed through the
function it moves to the NEXT grid line.
Expected Outcome:
What I am getting vs What I am trying to get:

I believe you should always increment the x position after rendering.
try this:
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile (a_canvas, type_of_tile, x, y, size)
x += size

Multiline solution (in case you didn't have it)
def process_single_line(a_canvas, line_of_pattern, left, top, size):
x = left
y = top
for char in line_of_pattern:
if char.isdigit():
type_of_tile = int(char)
draw_tile(a_canvas, type_of_tile, x, y, size)
x += size
lines = ['1x2xx1x2xx1x2xx1x2xxx', '3xxxx3xxxx3xxxx3xxxx']
for line_num, line in enumerate(lines):
process_single_line(canvas, line, 0, size*line_num, size)

Related

How to substitute unstressed vowel?

I have a CSV file with the following data:
bel.lez.za;bellézza
e.la.bo.ra.re;elaboràre
a.li.an.te;alïante
u.mi.do;ùmido
the first value is the word divided in syllables and the second is for the stress.
I'd like to merge the the two info and obtain the following output:
bel.léz.za
e.la.bo.rà.re
a.lï.an.te
ù.mi.do
I computed the position of the stressed vowel and tried to substitute the same unstressed vowel in the first value, but full stops make indexing difficult. Is there a way to tell python to ignore full stops while counting? or is there an easier way to perform it? Thx
After splitting the two values for each line I computed the position of the stressed vowels:
char_list=['ò','à','ù','ì','è','é','ï']
for character in char_list:
if character in value[1]:
position_of_stressed_vowel=value[1].index(character)
I'd suggest merging/aligning the two forms in parallel instead of trying to substitute things via indexing. The idea is to iterate through the plain form and take out one character from the accented form for every character from the plain form, keeping dots as they are.
(Or perhaps, the idea is to add the dots to the accented form instead of adding the accented characters to the syllabified form.)
def merge_accents(plain, accented):
output = ""
acc_chars = iter(accented)
for char in plain:
if char == ".":
output += char
else:
output += next(acc_chars)
return output
Test:
data = [['bel.lez.za', 'bellézza'],
['e.la.bo.ra.re', 'elaboràre'],
['a.li.an.te', 'alïante'],
['u.mi.do', 'ùmido']]
# Returns
# bel.léz.za
# e.la.bo.rà.re
# a.lï.an.te
# ù.mi.do
for plain, accented in data:
print(merge_accents(plain, accented))
Is there a way to tell python to ignore full stops while counting?
Yes, by implementing it yourself using an index lookup that tells you which index in the space-delimited string an index in the word is equivalent to:
i = 0
corrected_index = []
for char in value[0]:
if char != ".":
corrected_index.append(i)
i+=1
now, you can correct the index and replace the character:
value[0][corrected_index[position_of_stressed_vowel]] = character
Make sure to use UTF-16 as encoding for your "stressed vowel" characters to have a single index.
You can loop over the two halfs of the string, keep track of the index in the first half, excluding the dots and add the character at the tracked index from the second half of the string to a buffer (modified) string. Like the code below:
data = ['bel.lez.za;bellézza',
'e.la.bo.ra.re;elaboràre',
'a.li.an.te;alïante',
'u.mi.do;ùmido']
converted_data = []
# Loop over the data.
for pair in data:
# Split the on ";"
first_half, second_half = pair.split(';')
# Create variables to keep track of the current letter and the modified string.
current_letter = 0
modified_second_half = ''
# Loop over the letter of the first half of the string.
for current_char in first_half:
# If the current_char is a dot add it to the modified string.
if current_char == '.':
modified_second_half += '.'
# If the current_char is not a dot add the current letter from the second half to the modified string,
# and update the current letter value.
else:
modified_second_half += second_half[current_letter]
current_letter += 1
converted_data.append(modified_second_half)
print(converted_data)
data = ['bel.lez.za;bellézza',
'e.la.bo.ra.re;elaboràre',
'a.li.an.te;alïante',
'u.mi.do;ùmido']
def slice_same(input, lens):
# slices the given string into the given lengths.
res = []
strt = 0
for size in lens:
res.append(input[strt : strt + size])
strt += size
return res
# split into two.
data = [x.split(';') for x in data]
# Add third column that's the length of each piece.
data = [[x, y, [len(z) for z in x.split('.')]] for x, y in data]
# Put text and lens through function.
data = ['.'.join(slice_same(y, z)) for x, y, z in data]
print(data)
Output:
['bel.léz.za',
'e.la.bo.rà.re',
'a.lï.an.te',
'ù.mi.do']

Nested python loop selects complete column of 2d list instead of given index

I am writing a little level editor which generates a text tilemap to use in another program. I thought like this:
If I hover my mouse over a tile in a list and click, replace that index of the 2d list with the currently selected tile. Instead it replaces the entire column with the selected tile.
My code snippet responsible for checking if this happens:
import pygame
#Normal pygame window setup, class declaration and variable init
while True:
#Mouse position
m_x, m_y = pygame.mouse.get_pos()
events = pygame.event.get()
#Current tile the mouse is hovering over
self.tile_m_x = m_x // int(self.grid_params['size']) * int(self.grid_params['size'])
self.tile_m_y = m_y // int(self.grid_params['size']) * int(self.grid_params['size'])
#The part where it checks if the tile should be converted
for y, row in enumerate(self.tiles[:]):
for x, tile in enumerate(row):
if x == self.tile_m_x/int(self.grid_params['size']) and y == self.tile_m_y/int(self.grid_params['size']):
if m_btns[0]:
self.tiles[y][x] = self.current_selected_tile_type
I tried doing everything outside the loop, with the loop, inside the loop, above the loop, ..... You get the point i basically tried everything i could.
for y, row in enumerate(self.tiles[:]):
for x, tile in enumerate(row):
if x == self.tile_m_x/int(self.grid_params['size']) and y == self.tile_m_y/int(self.grid_params['size']):
if m_btns[0]:
self.tiles.loc[x,y] = self.current_selected_tile_type
try "=" not"==", to affect results

Robot return to origin

Question:
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once.
All moves have the same magnitude, so it ended up at the origin where it started.
Therefore, we return true.
I have the following solution, which seems to be wrong for sequences = "UD", which should return True. Could someone help me understand what I am doing wrong here and how I could fix it?
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
if __name__ == "__main__":
solution = Solution()
sequences = "UD"
print(solution.move_sequences(sequences))
This solution seems overthinking it by quite a bit. You can just make a counter of each of the 4 directions and figure out if you have the same number of Us relative to Ds and Ls relative to Rs. return s.count("U") == s.count("D") and s.count("L") == s.count("R") gives you a linear solution that can be optimized into a single pass with something like
from collections import Counter
d = Counter(moves)
return d["D"] == d["U"] and d["R"] == d["L"]
As for your code,
for moves in sequences:
return (solution.judge_circle(moves))
looks funny to me. Returning on the first iteration means the loop is pointless. moves here is misleadingly named -- it's only a single character "U". judge_circle already does a loop, so if you really want to brute-force it, you'll only want one loop over the sequence rather than two.
Your task is simple:
def judge_circle(moves):
if moves.lower().count('U') == moves.lower().count('D') and moves.lower().count('L') == moves.lower().count('R'):
return True
else:
return False
print(judge_circle('UD'))
You only have to check whether the number of 'ups' equals the numbers of 'downs', and 'lefts' equal 'rights'.
Ok, a part from refactor advices, you can fix your script in a easy way.
def move_sequences(self,sequences):
for moves in sequences:
return (solution.judge_circle(moves))
fails because you pass a string in sequences and the for loop cycles over the letters, passing every single letter to judge_circle.
Remove the for loop and pass sequences to judge_circle!

What would be the OOP way to solve this?

I am trying to solve this problem.
There is a robot starting at position (0, 0), the origin, on a 2D plane. Given a sequence of its moves, judge if this robot ends up at (0, 0) after it completes its moves.
The move sequence is represented by a string, and the character moves[i] represents its ith move. Valid moves are R (right), L (left), U (up), and D (down). If the robot returns to the origin after it finishes all of its moves, return true. Otherwise, return false.
Note: The way that the robot is "facing" is irrelevant. "R" will always make the robot move to the right once, "L" will always make it move left, etc. Also, assume that the magnitude of the robot's movement is the same for each move.
Example 1:
Input: moves = "UD"
Output: true
Explanation: The robot moves up once, and then down once. All moves have the same magnitude, so it ended up at the origin where it started. Therefore, we return true.
Example 2:
Input: moves = "LL"
Output: false
Explanation: The robot moves left twice. It ends up two "moves" to the left of the origin. We return false because it is not at the origin at the end of its moves.
Example 3:
Input: moves = "RRDD"
Output: false
Example 4:
Input: moves = "LDRRLRUULR"
Output: false
Even though I solved it using counters as well as conditionals, I would like to see how I could use Python's OOP components to solve this. How can I separate out the valid moves, which are R (right), L (left), U (up), and D (down) into separate methods for better abstraction, so that I could add more methods in future? How would the refactored code look like? This is the solution:
class Solution(object):
def judgeCircle(self, moves):
x = y = 0
for move in moves:
if move == 'U': y -= 1
elif move == 'D': y += 1
elif move == 'L': x -= 1
elif move == 'R': x += 1
return x == y == 0
Here's one way to do it by encapsulating the data and the code that acts on it — both key OOP features. Note though that using "getters" and "setters" is not "pythonic" (because they are generally unnecessary and can be added retroactively if the need ever arises for some reason).
class Solution:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
return x + self.x, y + self.y
WALKS = dict(U=Mover(0, -1), D=Mover(0, 1),
L=Mover(-1, 0), R=Mover(1, 0))
def judge_circle(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return x == y == 0
solution = Solution()
sequences = "UD", "LL", "RRDD", "LDRRLRUULR"
for moves in sequences:
print(solution.judge_circle(moves))

Unpacking in a for loop twice

I am trying to find the last iteration in a loop so that I can skip it. This is not ideal I'm aware, however my issue is separate.
I am new to python, but if I'm not mistaken, this for loop is in unpacking the variable letter. This makes the second run through of the for loop empty or in some sense broken. If my understanding here is in anyway incorrect, feel free to comment or edit.
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
last_iteration = this_iteration
this_iteration += 1
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
if this_iteration == last_iteration:
continue
this_iteration += 1
I tried unsuccessfully passing this in the second for loop, but the second for loop still does not run.
for letter in letters_positions:
Is there a way for me to repack the variables together for a second run through the loop?
UPDATE: This is CairoSVG, not my own code, but I'll try to post more context the best I can. letters_positions is taken from an svg file. The important two lines that precede my code are the following.
from .helpers import distance, normalize, point_angle, zip_letters
letters_positions = zip_letters(x, y, dx, dy, rotate, node.text)
Original CairoSVG code can be found on github here.
https://github.com/Kozea/CairoSVG/blob/master/cairosvg/text.py
Edit (example):
this_iteration = 0
letters_positions = list(letters_positions)
for [x, y, dx, dy, r], letter in letters_positions:
last_iteration = this_iteration
this_iteration += 1
this_iteration = 0
for [x, y, dx, dy, r], letter in letters_positions:
if this_iteration == last_iteration:
continue
this_iteration += 1
From helpers.py in the github link you posted:
# Incidentally, they say that this method returns a list with the current letter's positions.
# This isn't true - it is returning a generator.
# To return a list, the outermost parenthesis need to be replaced with square brackets,
# or by simply adding list before the parenthesis
# i.e. [...] or list(...)
def zip_letters(xl, yl, dxl, dyl, rl, word):
"""Returns a list with the current letter's positions (x, y and rotation).
E.g.: for letter 'L' with positions x = 10, y = 20 and rotation = 30:
>>> [[10, 20, 30], 'L']
Store the last value of each position and pop the first one in order to
avoid setting an x,y or rotation value that have already been used.
"""
# Notice the parenthesis below - this is a generator that gets exhausted after one iteration
return (
([pl.pop(0) if pl else None for pl in (xl, yl, dxl, dyl, rl)], char)
for char in word)
Thus, you empty it after the first iteration. Create a list or some other data structure out of it letters_positions = list(letters_positions), and then you can loop over it as many times as you'd like.
letters_positions is a sequence. If you don't want to iterate over the final element, do iterate over letters_positions[:-1]
EDIT: if you're using Python3, you might have to call list on letters_positions first

Categories