Skip to content

Small refactoring #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 78 additions & 165 deletions JigsawInferenceGizmo.py
Original file line number Diff line number Diff line change
@@ -1,191 +1,104 @@
# JIG code from Stand-up Maths video "Why don't Jigsaw Puzzles have the correct number of pieces?"
def hasBetterRatio(ratio, gridRatio, currentBest):
return abs(currentBest / ratio - 1) < abs(gridRatio / ratio - 1)

def low_factors(n):

def lowFactors(n):
# all the factors which are the lower half of each factor pair
lf = []
for i in range(1, int(n**0.5)+1):
if n % i == 0:
lf.append(i)
return lf
return [j for j in range(1, int(n ** 0.5) + 1) if n % j == 0]


def bestSolution(ratio, value):
currentBest = 0
idealSides = []
for factor in lowFactors(value):
wholeNumRatio = int(value / factor) # must be a whole number anyway
currentRatio = wholeNumRatio / factor
if currentBest == 0:
currentBest = currentRatio
idealSides = [factor, wholeNumRatio]
else:
if abs(currentRatio / ratio - 1) < abs(currentBest / ratio - 1):
currentBest = currentRatio
idealSides = [factor, wholeNumRatio]
return currentBest, idealSides


def jig(w,h,n,b=0):

# percentage we'll check in either direction
threshold = 0.1
def getBestSolutionForRange(debugMode, numPieces, penalty, ratio, valueRange, opinion):
gridRatio = 0
idealNSides = []
bestNumPieces = 0
best = 100
bestDeets = []
for value in valueRange:
currentBest, idealSides = bestSolution(ratio, value)

# the extra badness per piece
penalty = 1.005
if bestNumPieces == 0 or (hasBetterRatio(ratio, gridRatio, currentBest)):
bestNumPieces = value
gridRatio = currentBest
idealNSides = idealSides
pieceRatio = calcRatio(ratio, currentBest)
badnessScore = (penalty ** (abs(value - numPieces))) * pieceRatio

ratio = max(w,h)/min(w,h) # switched to be greater than 1

print("")
print(f"{w} by {h} is picture ratio {round(ratio,4)}")
print("")

max_cap = int((1+threshold)*n)
min_cap = int((1-threshold)*n)

up_range = [i for i in range(n,max_cap+1)]
down_range = [i for i in range(min_cap,n)] # do not want n included again
down_range.reverse()

# start at 100 which is silly high and then move down.
up_best = 100
up_best_deets = []
down_best = 100
down_best_deets = []

# I am using the run marker so I know if looking above or below n
run = 0

for dis_range in [up_range,down_range]:
best_n = 0
best_n_ratio = 0
best_n_sides = []

if run == 0:
print(f"Looking for >= {n} solutions:")
print("")
else:
print("")
print("Just out of interest, here are smaller options:")
print("")

for i in dis_range:
this_best = 0
for j in low_factors(i):
j2 = int(i/j) # must be a whole number anyway
this_ratio = j2/j
if this_best == 0:
this_best = this_ratio
best_sides = [j,j2]
else:
if abs(this_ratio/ratio - 1) < abs(this_best/ratio - 1):
this_best = this_ratio
best_sides = [j,j2]
yes = 0
if best_n == 0:
yes = 1
else:
if abs(this_best/ratio - 1) < abs(best_n_ratio/ratio - 1):
yes = 1
if yes == 1:
best_n = i
best_n_ratio = this_best
best_n_sides = best_sides
piece_ratio = max(ratio,this_best)/min(ratio,this_best)
badness_score = (penalty**(abs(i-n)))*piece_ratio
if run == 0:
if badness_score < up_best:
up_best = badness_score
up_best_deets = [best_n,best_n_sides,best_n_ratio]
else:
if badness_score < down_best:
down_best = badness_score
down_best_deets = [best_n,best_n_sides,best_n_ratio]
print(f"{best_n} pieces in {best_n_sides} (grid ratio {round(best_n_ratio,4)}) needs piece ratio {round(piece_ratio,4)}")
if b==1:
print(f"[badness = {round(badness_score,5)}]")


print(f"for {n} the best is {best_n} pieces with size {best_n_sides}")

run += 1
print("")
print(f"If I had to guess: I think it's {up_best_deets[0]} pieces.")
if badnessScore < best:
best = badnessScore
bestDeets = [bestNumPieces, idealNSides, gridRatio]
print(
f"{bestNumPieces} pieces in {idealNSides} (grid ratio {round(gridRatio, 4)}) needs piece ratio "
f"{round(pieceRatio, 4)}"
)
if debugMode is True:
print(f"[badness = {round(badnessScore, 5)}]")

if down_best < up_best:
print("")
print(f"BUT, fun fact, {down_best_deets[0]} would be even better.")
if opinion is True:
print(f"for {numPieces} the best is {bestNumPieces} pieces with size {idealNSides}", end='\n\n')

print("")
return 'DONE'
return best, bestDeets


def calcRatio(numA, numB):
return max(numA, numB) / min(numA, numB)

# I duplicated jig_v0 to make is easier to show in the video
def jig_v0(w,h,n,b=0):


def jig(width, height, numPieces, opinion=False, debugMode=False):
# percentage we'll check in either direction
threshold = 0.1

penalty = 1.005

ratio = max(w,h)/min(w,h) # switched to be greater than 1

print("")
print(f"{w} by {h} is picture ratio {round(ratio,4)}")
maxCap = int((1 + threshold) * numPieces)
minCap = int((1 - threshold) * numPieces)

ratio = calcRatio(width, height)

print("")

max_cap = int((1+threshold)*n)
min_cap = int((1-threshold)*n)
print(f"{width} by {height} is picture ratio {round(ratio, 4)}", end='\n\n')
print(f"Looking for >= {numPieces} solutions:", end='\n\n')

up_range = [i for i in range(n,max_cap+1)]
down_range = [i for i in range(min_cap,n)] # do not want n included again
down_range.reverse()
upRange = list(range(numPieces, maxCap + 1))
upBest, upBestDeets = getBestSolutionForRange(
debugMode, numPieces, penalty,
ratio, upRange, opinion
)

# start at 100 which is silly high and then move down.
up_best = 100
up_best_deets = []
down_best = 100
down_best_deets = []
print("Just out of interest, here are smaller options:", end='\n\n')

run = 0
downRange = list(range(minCap, numPieces)) # do not want n included again
downRange.reverse()

for dis_range in [up_range,down_range]:
best_n = 0
best_n_ratio = 0
best_n_sides = []
downBest, downBestDeets = getBestSolutionForRange(
debugMode, numPieces, penalty,
ratio, downRange, opinion
)

if run == 0:
print(f"Looking for >= {n} solutions:")
print("")
else:
if opinion is True:
print(f"If I had to guess: I think it's {upBestDeets[0]} pieces.")
if downBest < upBest:
print("")
print("Just out of interest, here are smaller options:")
print("")

for i in dis_range:
this_best = 0
for j in low_factors(i):
j2 = int(i/j) # must be a whole number anyway
this_ratio = j2/j
if this_best == 0:
this_best = this_ratio
best_sides = [j,j2]
else:
if abs(this_ratio/ratio - 1) < abs(this_best/ratio - 1):
this_best = this_ratio
best_sides = [j,j2]
yes = 0
if best_n == 0:
yes = 1
else:
if abs(this_best/ratio - 1) < abs(best_n_ratio/ratio - 1):
yes = 1
if yes == 1:
best_n = i
best_n_ratio = this_best
best_n_sides = best_sides
piece_ratio = max(ratio,this_best)/min(ratio,this_best)
badness_score = (penalty**(abs(i-n)))*piece_ratio
if run == 0:
if badness_score < up_best:
up_best = badness_score
up_best_deets = [best_n,best_n_sides,best_n_ratio]
else:
if badness_score < down_best:
down_best = badness_score
down_best_deets = [best_n,best_n_sides,best_n_ratio]
print(f"{best_n} pieces in {best_n_sides} (grid ratio {round(best_n_ratio,4)}) needs piece ratio {round(piece_ratio,4)}")
if b==1:
print(f"[badness = {round(badness_score,5)}]")



run += 1

print(f"BUT, fun fact, {downBestDeets[0]} would be even better.", end='\n\n')

print("")
return 'DONE'


if __name__ == '__main__':
print(jig(33, 22.8, 1000, opinion=True))