I am running into an infinite loop in this code. It should break out if you click in the desired range, however it goes into an infinite loop displaying the current position of the turtle in the row and column format.
def wait_for_click():
turt.penup()
wn.onclick(turt.goto)
wn.listen()
pos = [-1,-1]
row = -1
column = -1
while row > 8 or row < 0 or column > 8 or column < 0:
row = ((turt.ycor()-turt.ycor()%75)+75)/75 + 4
column = ((turt.xcor()-turt.xcor()%75)+75)/75 + 4
pos[0] = row
pos[1] = column
print(pos)
I think your basic approach is wrong: don't loop waiting for the turtle to show up somewhere interesting, instead use the click handler to test where the turtle showed up:
from math import ceil
from turtle import Turtle, Screen
CELLS = 8
CELL_SIZE = 75
STAMP_SIZE = 20
def click_handler(x, y):
screen.onclick(None)
yertle.goto(x, y)
if 0 < x < CELLS and 0 < y < CELLS:
position = [ceil(x), ceil(y)]
print(position)
screen.onclick(click_handler)
screen = Screen()
screen.setup(CELL_SIZE * (CELLS + 2), CELL_SIZE * (CELLS + 2))
screen.setworldcoordinates(-1, -1, CELLS + 1, CELLS + 1)
screen.onclick(click_handler)
marker = Turtle(shape="square")
marker.penup()
marker.turtlesize(CELL_SIZE / STAMP_SIZE)
marker.color("gray", "white")
for x in range(0, CELLS):
for y in range(0, CELLS):
marker.goto(x + 0.5, y + 0.5)
marker.stamp()
marker.color(*marker.color()[::-1])
marker.color(*marker.color()[::-1])
yertle = Turtle(shape="circle")
yertle.speed("fastest")
yertle.penup()
screen.mainloop()
I've thrown in code to show the grid so you can see that where you click matches the printed output. I used setworldcoordinates() to simplify the problem with the side effect that I've given you a larger border.
The lower left cell is [1, 1] and the upper right is [8, 8] -- you may want to do some math to switch these around.
Related
question up front
def shade_func(color, offset):
return tuple([int(c * (1 - offset)) for c in color])
def tint_func(color, offset):
return tuple([int(c + (255 - c) * offset) for c in color])
def tone_func(color, offset):
return tuple([int(c * (1 - offset) + 128 * offset) for c in color])
given an objective over a collection of colors that returns the least distance to a target color, how do I ensure that basinhopping isn't better than minimization in scikit learn?
I was thinking that, for any one color, there will be up to 4 moments in a v-shaped curve, and so only one minimum. if the value with offset zero is itself a minimum, maybe it could be 5. Am I wrong? In any case each is a single optimum, so if we are only searching one color at a time, no reason to use basinhopping.
If we instead use basinhopping to scan all colors at once (we can scale the two different dimensions, in fact this is where the idea of a preprocessor function first came from), it scans them, but does not do such a compelling just of scanning all colors. Some colors it only tries once. I think it might completely skip some colors with large enough sets.
details
I was inspired by way artyclick shows colors and allows searching for them. If you look at an individual color, for example mauve you'll notice that it prominently displays the shades, tints, and tones of the color, rather like an artist might like. If you ask it for the name of a color, it will use a hidden unordered list of about a thousand color names, and some javascript to find the nearest colorname to the color you chose. In fact it will also show alternatives.
I noticed that quite often a shade, tint or tone of an alternative (or even the best match) was often a better match than the color it provided. For those who don't know about shade, tint and tone, there's a nice write up at Dunn Edward's Paints. It looks like shade and tint are the same but with signs reversed, if doing this on tuples representing colors. For tone it is different, a negative value would I think saturate the result.
I felt like there must be authoritative (or at least well sourced) colorname sources it could be using.
In terms of the results, since I want any color or its shade/tint/tone, I want a result like this:
{'color': '#aabbcc',
'offset': {'type': 'tint', 'value': 0.31060384614807254}}
So I can return the actual color name from the color, plus the type of color transform to get there and the amount of you have to go.
For distance of colors, there is a great algorithm that is meant to model human perception that I am using, called CIEDE 2000. Frankly, I'm just using a snippet I found that implements this, it could be wrong.
So now I want to take in two colors, compare their shade, tint, and tone to a target color, and return the one with the least distance. After I am done, I can reconstruct if it was a shade, tint or tone transform from the result just by running all three once and choosing the best fit. With that structure, I can iterate over every color, and that should do it. I use optimization because I don't want to hard code what offsets it should consider (though I am reconsidering this choice now!).
because I want to consider negatives for tone but not for shade/tint, my objective will have to transform that. I have to include two values to optimize, since the objection function will need to know what color to transform (or else the result will give me know way of knowing which color to use the offset with).
so my call should look something like the following:
result = min(minimize(objective, (i,0), bounds=[(i, i), (-1, 1)]) for i in range(len(colors)))
offset_type = resolve_offset_type(result)
with that in mind, I implemented this solution, over the past couple of days.
current solution
from scipy.optimize import minimize
import numpy as np
import math
def clamp(low, x, high):
return max(low, min(x, high))
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def rgb_to_hex(rgb):
return '#{:02x}{:02x}{:02x}'.format(*rgb)
def rgb_to_lab(color):
# Convert RGB to XYZ color space
R = color[0] / 255.0
G = color[1] / 255.0
B = color[2] / 255.0
R = ((R + 0.055) / 1.055) ** 2.4 if R > 0.04045 else R / 12.92
G = ((G + 0.055) / 1.055) ** 2.4 if G > 0.04045 else G / 12.92
B = ((B + 0.055) / 1.055) ** 2.4 if B > 0.04045 else B / 12.92
X = R * 0.4124 + G * 0.3576 + B * 0.1805
Y = R * 0.2126 + G * 0.7152 + B * 0.0722
Z = R * 0.0193 + G * 0.1192 + B * 0.9505
return (X,Y,Z)
def shade_func(color, offset):
return tuple([int(c * (1 - offset)) for c in color])
def tint_func(color, offset):
return tuple([int(c + (255 - c) * offset) for c in color])
def tone_func(color, offset):
return tuple([int(c * (1 - offset) + 128 * offset) for c in color])
class ColorNameFinder:
def __init__(self, colors, distance=None):
if distance is None:
distance = ColorNameFinder.ciede2000
self.distance = distance
self.colors = [hex_to_rgb(color) for color in colors]
#classmethod
def euclidean(self, left, right):
return (left[0] - right[0]) ** 2 + (left[1] - right[1]) ** 2 + (left[2] - right[2]) ** 2
#classmethod
def ciede2000(self, color1, color2):
# Convert color to LAB color space
lab1 = rgb_to_lab(color1)
lab2 = rgb_to_lab(color2)
# Compute CIE 2000 color difference
C1 = math.sqrt(lab1[1] ** 2 + lab1[2] ** 2)
C2 = math.sqrt(lab2[1] ** 2 + lab2[2] ** 2)
a1 = math.atan2(lab1[2], lab1[1])
a2 = math.atan2(lab2[2], lab2[1])
dL = lab2[0] - lab1[0]
dC = C2 - C1
dA = a2 - a1
dH = 2 * math.sqrt(C1 * C2) * math.sin(dA / 2)
L = 1
C = 1
H = 1
LK = 1
LC = math.sqrt(math.pow(C1, 7) / (math.pow(C1, 7) + math.pow(25, 7)))
LH = math.sqrt(lab1[0] ** 2 + lab1[1] ** 2)
CB = math.sqrt(lab2[1] ** 2 + lab2[2] ** 2)
CH = math.sqrt(C2 ** 2 + dH ** 2)
SH = 1 + 0.015 * CH * LC
SL = 1 + 0.015 * LH * LC
SC = 1 + 0.015 * CB * LC
T = 0.0
if (a1 >= a2 and a1 - a2 <= math.pi) or (a2 >= a1 and a2 - a1 > math.pi):
T = 1
else:
T = 0
dE = math.sqrt((dL / L) ** 2 + (dC / C) ** 2 + (dH / H) ** 2 + T * (dC / SC) ** 2)
return dE
def __factory_objective(self, target, preprocessor=lambda x: x):
def fn(x):
print(x, preprocessor(x))
x = preprocessor(x)
color = self.colors[x[0]]
offset = x[1]
bound_offset = abs(offset)
offsets = [
shade_func(color, bound_offset),
tint_func(color, bound_offset),
tone_func(color, offset)]
least_error = min([(right, self.distance(target, right)) \
for right in offsets], key = lambda x: x[1])[1]
return least_error
return fn
def __resolve_offset_type(self, sample, target, offset):
bound_offset = abs(offset)
shade = shade_func(sample, bound_offset)
tint = tint_func(sample, bound_offset)
tone = tone_func(sample, offset)
lookup = {}
lookup[shade] = "shade"
lookup[tint] = "tint"
lookup[tone] = "tone"
offsets = [shade, tint, tone]
least_error = min([(right, self.distance(target, right)) for right in offsets], key = lambda x: x[1])[0]
return lookup[least_error]
def nearest_color(self, target):
target = hex_to_rgb(target)
preprocessor=lambda x: (int(x[0]), x[1])
result = min(\
[minimize( self.__factory_objective(target, preprocessor=preprocessor),
(i, 0),
bounds=[(i, i), (-1, 1)],
method='Powell') \
for i, color in enumerate(self.colors)], key=lambda x: x.fun)
color_index = int(result.x[0])
nearest_color = self.colors[color_index]
offset = preprocessor(result.x)[1]
offset_type = self.__resolve_offset_type(nearest_color, target, offset)
return {
"color": rgb_to_hex(nearest_color),
"offset": {
"type": offset_type,
"value": offset if offset_type == 'tone' else abs(offset)
}
}
let's demonstrate this with mauve. We'll define a target that is similar to a shade of mauve, include mauve in a list of colors, and ideally we'll get mauve back from our test.
colors = ['#E0B0FF', '#FF0000', '#000000', '#0000FF']
target = '#DFAEFE'
agent = ColorNameFinder(colors)
agent.nearest_color(target)
we do get mauve back:
{'color': '#e0b0ff',
'offset': {'type': 'shade', 'value': 0.0031060384614807254}}
the distance is 0.004991238317138219
agent.distance(hex_to_rgb(target), shade_func(hex_to_rgb(colors[0]), 0.0031060384614807254))
why use Powell's method?
in this arrangement, it is simply the best. No other method that uses bounds did a good job of scanning positives and negatives, and I had mixed results using the preprocessor to scale the values back to negative with bounds of (0,2).
I do notice that in the sample test, a range between about 0.003 and 0.0008 seems to produce the same distance, and that the values my approach considers includes a large number of these. is there a more efficient solution?
If I'm wrong, please let me know.
correctness of the color transformations
what is adding a negative amount of white? (in the case of a tint) I was thinking it is like adding a positive amount of black -- ie a shade, with signs reversed.
my implementation is not correct:
agent.distance(hex_to_rgb(target), shade_func(hex_to_rgb(colors[0]), 0.1)) - agent.distance(hex_to_rgb(target), tint_func(hex_to_rgb(colors[0]), -0.1))
produces 0.3239904390784106 instead of 0.
I'll probably be fixing that soon
My having troubles understanding why is my code not working like I think it should.
This function is supposed to fill up a 2 dimensional list with 1 instead of 0 given some parameters:
x = to tell it where to start to fill up on the x axis
y = to tell it where to start to fill up on the y axis
length = length of the rectangle
width = width of the rectangle
rotation = boolean to tell if the rectangle should be drawn
vertically (if True) or horizontally (if False)
So I call drawLoop() that will call drawVertical() that calls drawHorizon() afterward
This function is aimed at receiving multiple rectangle, but my problem lies when the first is added.
Here is the code:
# Create a 2D list of 120 * 60
thisBox = [["0"] * 120] * 60
# Filler function
def drawLoop(x, y, length, width, rotation):
def drawHorizon(x, y, length, width, rotation, row):
drawIndexH = 0
while drawIndexH < len(row):
if rotation == False and drawIndexH >= x and drawIndexH < x + length:
row[drawIndexH] = "1"
drawIndexH += 1
elif rotation == True and drawIndexH >= x and drawIndexH < x + width:
row[drawIndexH] = "1"
drawIndexH += 1
else:
return
def drawVertical(x, y, length, width, rotation):
drawIndexV = 0
while drawIndexV < len(thisBox):
if rotation == False and drawIndexV >= y and drawIndexV < y + width:
drawHorizon(x, y, length, width, rotation, thisBox[drawIndexV])
drawIndexV += 1
elif rotation == True and drawIndexV >= y and drawIndexV < y + length:
drawHorizon(x, y, length, width, rotation, thisBox[drawIndexV])
drawIndexV += 1
else:
drawIndexV += 1
# Launch vertical drawing
drawVertical(x, y, length, width, rotation)
# Launch main function
drawLoop(0, 0, 70, 50, False)
As of now, the 120 * 60 space is empty, so by calling the main function with drawloop(0, 0, 70, 50, False)on the last line, I'm supposed to see a 70 * 50 rectangle drawn at the position (0, 0). So that out of the 7200 0 (120 * 60) I should see only 3700 left (7200 - (70 * 50))
So the function is divided into 2 other functions: drawVertical(x, y, length, width, rotation) that will draw vertically and drawHorizon(x, y, length, width, rotation, row) that will draw horizontally first.
But somehow, at the first iteration of drawVertical(...), all the rows are being filled up in one iteration and do not stop at the exit condition: it should stop at y = 50 (the width) when if rotation == False and drawIndexV >= y and drawIndexV < y + width: of drawVertical(...) because drawIndexV < y + width should stop at 50. But it does not and I have no clue why.
Even if I tell drawVertical(...) to stop the loop at the first iteration with while drawIndexV < 2:, all the rows are being filled up.
So horizontally I have the expected result, but I never have it vertically. Can anybody spot my mistake? Many thanks in advance!
Ben.
The problem is indeed that
thisBox = [["0"] * 120] * 60
creates a list with 60 times the same element, a list with 120 "0", as the following code snippet shows:
for r in range(60):
print(id(thisBox[r]))
for which a sample output is:
140706579889408
140706579889408
140706579889408
140706579889408
140706579889408
140706579889408
...
Updating any element on any row will update the same element on every row, since every row is the same unique list object.
To avoid the issue, one needs to ensure that each of the enclosed lists (the 60 lists, each of which contains 120 "0") is a separate list object, distinct from all the other enclosed lists, i.e., has its own id.
If familiar with numpy (numpy.zeros), and depending on the exact requirements, resorting to numpy arrays could be a solution. If using numpy is an instance of "shooting a bird with a cannonball", one alternative is using list comprehensions to initialise the list:
thisBox = [["0" for c in range(120)] for r in range(60)]
Running the same code as before confirms that each list of 120 "0" now has its own id, i.e., each row is a separate list:
for r in range(60):
print(id(thisBox[r]))
140185522518784
140185522481600
140185522519680
140185522482560
140185503364672
...
(Would have added the solution described above, using list comprehensions, as a comment, but was barred from doing so because of insufficient "reputation" points)
I have found my problem (Merci Philippe!!!)
When I do this:
# Create a 2D list of 120 * 60
thisBox = [["0"] * 120] * 60
I'm actually creating a list of the same element 60 times. They all have the same memory allocation. So if I modify one, I modify them all. That's why one iteration of drawVertical(...) modified the whole 60 rows.
I have to arrange and/or fit 2d tiles into a 2d square or rectangular plane with AI algorithm using python program. Each tile has a length and width. For example if a plane is 4x3 and set of tiles is
S={(2,3),(1,2),(2,2)}
these tiles can be rotated 90 degrees in order to fit the matrix.
input
first line contains length and width of the plane
second line number of tiles
and then the length,width of the subsequent tiles
but the inputs should be tab seperated
for eg
4 3
3
2 3
1 2
2 2
output
for eg
1 1 2 2
1 1 3 3
1 1 3 3
I have trouble solving this as i have to use only standard libraries in python no NumPy and no CSP library
~Edit 2`
my code so far I cant figure out how to add algorithm without csp library or to generate grid
from sys import stdin
a = stdin.readline()
x = a.split()
rectangular_plane = [[0] * int(x[0]) for i in range(int(x[1]))]
num_of_rectangles = stdin.readline()
r_widths = []
r_lengths= []
for l in range(int(num_of_rectangles)):
b = stdin.readline()
y = b.split()
r_lengths.insert(l,y[0])
r_widths.insert(l,y[1])
I've solved task with backtracking approach and without any non-standard modules.
Try it online!
import sys
nums = list(map(int, sys.stdin.read().split()))
pw, ph = nums[0:2]
ts = list(zip(nums[3::2], nums[4::2]))
assert len(ts) == nums[2]
if sum([e[0] * e[1] for e in ts]) != pw * ph:
print('Not possible!')
else:
def Solve(*, it = 0, p = None):
if p is None:
p = [[0] * pw for i in range(ph)]
if it >= len(ts):
for e0 in p:
for e1 in e0:
print(e1, end = ' ')
print()
return True
for tw, th in [(ts[it][0], ts[it][1]), (ts[it][1], ts[it][0])]:
zw = [0] * tw
ow = [it + 1] * tw
for i in range(ph - th + 1):
for j in range(pw - tw + 1):
if all(p[k][j : j + tw] == zw for k in range(i, i + th)):
for k in range(i, i + th):
p[k][j : j + tw] = ow
if Solve(it = it + 1, p = p):
return True
for k in range(i, i + th):
p[k][j : j + tw] = zw
return False
if not Solve():
print('Not possible!')
Example input:
4 3
3
2 3
1 2
2 2
Output:
1 1 2 2
1 1 3 3
1 1 3 3
I'm going over the book 'automate the boring stuff with python' and cannot understanding a simple expression with the % operator. The expression is leftCoord = (x - 1) % WIDTH which on the first iteration of the loop evaluates to (0 - 1) % 60. In my mind the % operator should evaluate to the remainder of a division. Why does it evaluate to 9?
This is the part of the program that precedes the expression in question:
import random,time,copy
WIDTH = 60
HEIGHT = 20
# Create a list of list for the cells:
nextCells = []
for x in range(WIDTH):
column = [] # Create a new column.
for y in range(HEIGHT):
if random.randint(0,1) == 0:
column.append('#') # Add a living cell.
else:
column.append(' ') # Add a dead cell.
nextCells.append(column) # nextCells is a list of column lists.
while True: # Main program loop.
print('\n\n\n\n\n') # Separate each step with newlines.
currentCells = copy.deepcopy(nextCells)
# Print currentCells on the screen:
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end='') # Print the # or space.
print() # Print a newline at the end of the row.
# Calculate the next step's cells based on current step's cells:
for x in range(WIDTH):
for y in range(HEIGHT):
# Get neighboring coordinates:
# % WIDTH ensures leftCoord is always between 0 and WIDTH -1
leftCoord = (x - 1) % WIDTH
rightCoord = (x + 1) % WIDTH
aboveCoord = (y - 1) % HEIGHT
belowCoord = (y + 1) % HEIGHT
For the sake of example, let's assume that you're using a table of 10x10.
The % operator isn't so intuitive when the first number is smaller than the second. Try going into the interactive python shell and running 4 % 10. Try 8 % 10. Notice how you always get the same number back? That's because the answer to the division is 0... with your whole number being left over as remainder. For most numbers in the table, the modulus doesn't do anything at all.
Now try -1 % 10 (simulating what this would do for the top row). It gives you 9, indicating the bottom row. If you run 10 % 10 (simulating the bottom row), it gives you 0, indicating the top row. Effectively, this makes the table "wrap"... the cells in the top row affect the bottom and vice versa. It also wraps around the sides.
Hope this helps!
I have a question revolving around what would be a viable approach to placing out random-sized squares on a symmetrical, non-visible grid on a tkinter-canvas. I'm going to explain it quite thoroughly as it's a somewhat proprietary problem.
This far I've tried to solve it mostly mathematically. But I've found it to be quite a complex problem, and it seems reasonable that there would be a better approach to take it on than what I've tried.
In its most basic form the code looks like this:
while x_len > canvas_width:
xpos = x_len + margin
squares[i].place(x=xpos, y=ypos)
x_len += square_size + space
i += 1
x_len is the total width of all the squares on a given row, and resets when exiting the while-loop (eg. when x_len > window width), among with xpos (the position on X), as well as altering Y-axis to create a new row.
When placing same-size squares it looks like this:
So far so good.
However when the squares are of random-size it looks like this (at best):
The core problem, beyond that the layout can be quite unpredictable, is that the squares aren't centered to the "invisible grid" - because there is none.
So to solve this I've tried an approach where I use a fixed distance and a relative distance based on every given square. This yields satisficing results for the Y-axis on the first row, but not on the X-axis, nor the following rows on Y.
See example (where first row is centered on Y, but following rows and X is not):
So with this method I'm using a per-square alteration in both Y- and X-axis, based on variables that I fetch from a list that contain widths for all of the generated squares.
In it's entirety it looks like this (though it's work in progress so it's not very well optimized):
square_widths = [60, 75, 75, 45...]
space = square_size*0.5
margin = (square_size+space)/2
xmax = frame_width - margin - square_size
xmin = -1 + margin
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
m_ypos = 0
extra_x = 0
while len(squares) <= 100:
n=-1
# row_ypos alters y for every new row
row_ypos += 200-square_widths[n]/2
# this if-statement is not relevant to the question
if x < 0:
n=0
xpos = x
extra_x = x
x_len = z
while x_len < xmax:
ypos = row_ypos
extra_x += 100
ypos = row_ypos + (200-square_widths[n])/2
xpos = extra_x + (200-square_widths[n])/2
squares[i].place(x=xpos, y=ypos)
x_len = extra_x + 200
i += 1
n += 1
What's most relevant here is row_ypos, that alters Y for each row, as well as ypos, that alters Y for each square (I don't have a working calculation for X yet). What I would want to achieve is a similar result that I get for Y-axis on the first row; on all rows and columns (eg. both in X and Y). To create a symmetrical grid with squares of different sizes.
So my questions are:
Is this really best practice to solve this?
If so - Do you have any tips on decent calculations that would do the trick?
If not - How would you approach this?
A sidenote is that it has to be done "manually" and I can not use built-in functions of tkinter to solve it.
Why don't you just use the grid geometry manager?
COLUMNS = 5
ROWS = 5
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
This will line everything up, but the randomness may make the rows and columns different sizes. You can adjust that with the row- and columnconfigure functions:
import tkinter as tk
from random import randint
COLUMNS = 10
ROWS = 5
class GUI(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
labels = []
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
labels.append(l)
self.update() # draw everything
max_width = max(w.winfo_width() for w in labels)
max_height = max(w.winfo_height() for w in labels)
for column in range(self.grid_size()[0]):
self.columnconfigure(col, minsize=max_width) # set all columns to the max width
for row in range(self.grid_size()[1]):
self.rowconfigure(row, minsize=max_height) # set all rows to the max height
def main():
root = tk.Tk()
win = GUI(root)
win.pack()
root.mainloop()
if __name__ == "__main__":
main()
I found the culprit that made the results not turn out the way expected, and it wasn't due to the calculations. Rather it turned out that the list I created didn't put the squares in correct order (which I should know since before).
And so I fetched the width from the raw data itself, which makes a lot more sense than creating a list.
The function now looks something like this (again, it's still under refinement, but I just wanted to post this, so that people don't waste their time in coming up with solutions to an already solved problem :)):
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
while len(squares) <= 100:
n=0
if y > 1:
ypos -= max1 + 10
if y < 0:
if ypos < 0:
ypos=10
else:
ypos += max1 + 10 #+ (max1-min1)/2
if x < 0:
n=0
xc=0
xpos = x
x_len = z
while x_len < xmax:
yc = ypos + (max1-squares[i].winfo_width())/2
if xpos <= 0:
xpos = 10
else:
xpos += max1 + 10
xc = xpos + (max1-squares[i].winfo_width())/2
squares[i].place(x=xc, y=yc)
x_len += max1 + 10
print (x_len)
i += 1
n += 1