+ 1
- 0

.gitignore
View File
@@ -0,0 +1 @@ | |||

hack/* |

+ 2
- 1

README.md
View File
@@ -1,2 +1,3 @@ | |||

# foobar | |||

My Solutions for the [Google Foobar](https://google.com/foobar) challenge, full | |||

writeup available [here](https://govanify.com/post/foobar/) |

+ 111
- 0

bringing_a_gun_to_a_guard_fight.py
View File
@@ -0,0 +1,111 @@ | |||

from math import atan2, sqrt | |||

""" | |||

The first thing you had to realize in order to solve this challenge is that you | |||

could simply mirror the room and check if the beam gets where you want. Let me | |||

show you: | |||

In our case let us make a room of dimension [3,2], with the position of the | |||

target at [1,2]. For the sake of simplicity let us imagine the beam is shot at | |||

[1,2] and so we are trying to shot ourselves. We get something like this: | |||

----- | |||

|*xx| | |||

|xxx| | |||

----- | |||

If we shoot with a vector of [1,0], aka straight ahead, we will get this result: | |||

----- | |||

|*~~| | |||

|xxx| | |||

----- | |||

We can see that the beam gets back to you directly, thanks to the reflection, | |||

and that the goal is achieved! But there is another way to do that: | |||

----- ----- | |||

|*~~| |~~*| | |||

|xxx| |xxx| | |||

----- ----- | |||

We can here realize that, by putting a mirror version of the room where the beam | |||

gets, we can make a straight line instead of calculating the reflection and see | |||

that it gets to our original target, [1,2], being ourselves! | |||

The good thing with that is that it also allows us to determine the distance | |||

needed by the beam to get through, and thus we can mirror the rooms up to the | |||

maximum distance easily. | |||

The next thing to realize was that room mirrors in a pattern, see the below | |||

diagram: | |||

----- | |||

|*xx| | |||

|xxx| | |||

----- | |||

[...] ----- [...] | |||

|xxx| | |||

|*xx| | |||

----- | |||

----- ----- ----- ----- ----- | |||

|*xx| |xx*| |*xx| |xx*| |*xx| | |||

|xxx| |xxx| |xxx| |xxx| |xxx| | |||

----- ----- ----- ----- ----- | |||

----- | |||

|xxx| | |||

|*xx| | |||

----- | |||

[...] ----- [...] | |||

|*xx| | |||

|xxx| | |||

----- | |||

x always repeats in the same way, and so does y | |||

Thus we need to figure out how to calculate how to mirror one room twice and we | |||

can make an entire atlas of mirrored rooms! | |||

In our case though the function below only calculates the mirrors of the point | |||

of coordinates node across an atlas of length (distance*2)^2 | |||

""" | |||

def mirror_atlas(node, dimensions, distance): | |||

node_mirrored=[] | |||

for i in range(len(node)): | |||

points=[] | |||

# since it is floored we add 1, and python stops before the upper bound | |||

# so +2 in our case | |||

for j in range(-(distance//dimensions[i])-1, (distance//dimensions[i]+2)): | |||

points.append(get_mirror(j, node[i], dimensions[i])) | |||

node_mirrored.append(points) | |||

return node_mirrored | |||

# this simply calculates the mirror nth of a point in a 1D space | |||

def get_mirror(mirror, coordinates, dimensions): | |||

res=coordinates | |||

mirror_rotation=[2*coordinates, 2*(dimensions-coordinates)] | |||

if(mirror<0): | |||

for i in range(mirror, 0): | |||

res-=mirror_rotation[(i+1)%2] | |||

else: | |||

for i in range(mirror, 0, -1): | |||

res+=mirror_rotation[i%2] | |||

return res | |||

# pretty simple too: we get the angle at which the beam is needed to be to get | |||

# to the guard and store its distance. First we do that to yourself, then to the | |||

# guard and if the distance for the guard is lower than yourself then he'll be | |||

# hit first by the beam. | |||

def answer(dimensions, your_position, guard_position, distance): | |||

mirrored = [mirror_atlas(your_position, dimensions, | |||

distance),mirror_atlas(guard_position, dimensions, distance)] | |||

res=set() | |||

angles_dist={} | |||

for i in range(0, len(mirrored)): | |||

for j in mirrored[i][0]: | |||

for k in mirrored[i][1]: | |||

# position of the beam to touch the node, either yourself or the | |||

# guard | |||

beam=atan2((your_position[1]-k), (your_position[0]-j)) | |||

# pythagorean theorem gets you the hypothenuse given two points | |||

# of a triangle, which is basically the distance between those | |||

# two points | |||

l=sqrt((your_position[0]-j)**2 + (your_position[1]-k)**2) | |||

if [j,k] != your_position and distance >= l: | |||

if((beam in angles_dist and angles_dist[beam] > l) or beam not in angles_dist): | |||

if i == 0: | |||

angles_dist[beam] = l | |||

else: | |||

angles_dist[beam] = l | |||

# we could find a better result later so let's just | |||

# put that in a set to sort out duplicates | |||

res.add(beam) | |||

return len(res) |

+ 69
- 0

distract_the_guards.py
View File
@@ -0,0 +1,69 @@ | |||

from fractions import gcd | |||

""" | |||

Ah, combinatronics maths, always fun. | |||

Let's suppose we have two guards starting with a and b bananas, where we assume a > b | |||

Let k be the largest positive integer such that a > (2^k - 1)b | |||

If a = b(2^k - 2^(m-1) - 1) or a = b(2^(k+1) - 2^m - 1) for some integer m between 0 and k included | |||

then we can match the two guards to play an infinite round of thumb wrestling. | |||

We can thus deduce the formula below after development. | |||

""" | |||

def loops(x, y): | |||

res = (x+y)/gcd(x,y) | |||

return bool(res & (res - 1)) | |||

""" | |||

This function might looks like a bottleneck but we need to find a | |||

way to update node sizes either way, so i don't think a better way | |||

is possible at the time of writing. I might totally be wrong but for our use | |||

case arrays won't be bigger than 100 so *shrug* | |||

""" | |||

def remove(guards, ref): | |||

for i in range(len(guards)): | |||

j = 0 | |||

while j < len(guards[i]): | |||

if(guards[i][j]==ref): | |||

guards[i].pop(j) | |||

j+=1 | |||

guards[ref]=[-1] | |||

def answer(banana_list): | |||

guards= [[] for i in range(len(banana_list))] | |||

bad=0 | |||

for i in range(len(guards)): | |||

for j in range(len(guards)): | |||

if(loops(banana_list[i], banana_list[j])): | |||

guards[i].append(j) | |||

to_process=len(banana_list) | |||

while(to_process>0): | |||

# we first find the lesser used number | |||

min_num=0 | |||

for i in range(len(guards)): | |||

if(i!=0 and (len(guards[i])<len(guards[min_num]) or guards[min_num] | |||

== [-1]) and guards[i]!=[-1]): | |||

min_num=i | |||

# if this node absolutely cannot be paired we purge it | |||

if((len(guards[min_num])) == 0 or (len(guards[min_num])==1 and | |||

guards[min_num][0] == guards[min_num]) and guards[min_num] != | |||

[-1]): | |||

remove(guards, min_num) | |||

to_process-=1 | |||

bad+=1 | |||

else: | |||

# now we deduce the lesser used node associated to this number | |||

min_node=guards[min_num][0] | |||

for i in range(len(guards[min_num])): | |||

if(i!=0 and guards[min_num][i]!=min_num and len(guards[guards[min_num][i]])<len(guards[min_node])): | |||

min_node=guards[min_num][i] | |||

# if we fail here infinite loop, which is bad because that means | |||

# only processed nodes exist and we haven't exited; logic bug | |||

if(guards[min_node]!=[-1]): | |||

remove(guards, min_num) | |||

remove(guards, min_node) | |||

to_process-=2 | |||

return bad |

+ 34
- 0

en_route_salute.py
View File
@@ -0,0 +1,34 @@ | |||

""" | |||

Almost no computation there, we get positions of arrows depending of their type | |||

sorted and then do two nested loops to detect collisions, can't think of | |||

anything better rn | |||

""" | |||

def answer(s): | |||

# readme.txt | |||

if(len(s) > 100 or len(s) < 1): | |||

raise ValueError('Height is outside of bounds') | |||

# we keep only the arrows and cast it into a list ot make it iterable easily | |||

s = list(s.replace("-","")) | |||

left = [] | |||

right = [] | |||

res=0 | |||

for i in range(0,len(s)): | |||

if s[i] == '<': | |||

left.append(i) | |||

if s[i] == '>': | |||

right.append(i) | |||

# two nested loops to detect collision | |||

for i in right: | |||

for y in left: | |||

if i < y: | |||

res+=1 | |||

for i in left: | |||

for y in right: | |||

if y < i: | |||

res+=1 | |||

return res |

+ 8
- 0

end.py
View File
@@ -0,0 +1,8 @@ | |||

import base64 | |||

encrypted="HEYGAwIKCxQSUlZbSUkAExAXFU5CR0YWGQ0FCwYGABNGSVRHRhAFFQwLCgQRUU1JSQIHExkTHR1AQU9WRgAABBMQEggLAgJGWVZGCA0PCBAABAQLCRVSVltJSRIPGRkCAgsDRllWRhsPBQMcAhJOTl1BUgUADwtATVVRBwYBQEFPVkYeBwlAUgs=" | |||

my_eyes=str.encode("gauvain") | |||

decoded=base64.b64decode(encrypted) | |||

decrypted="" | |||

for i in range(0,len(decoded)): | |||

decrypted+=chr((my_eyes[i%len(my_eyes)] ^ decoded[i])) | |||

print(decrypted) |

+ 41
- 0

expanding_nebula.py
View File
@@ -0,0 +1,41 @@ | |||

""" | |||

The hard part of this challenge was figuring out how to make it in the time | |||

limit, much like the old XOR challenge. This one though was on another | |||

optimization technique, memoization(I see what you did there Google, challenges | |||

using the skills you ask for interviews on your website). | |||

Hashing the entire list would have been too long, even as a tuple, so instead we | |||

had to be clever and use another trick: building an history of all the moves that | |||

happened. | |||

This allows us, given a pair of row and column a and b, to avoid having to do | |||

extreme amount of recursive computation to get a result, as long as an history | |||

of the last (past grid row + current move) moves is saved. | |||

The rest of the problem is fairly easy: we recursively try out all possible | |||

combinations for this grid and save the number of correct ones we could get. | |||

""" | |||

def answer(state, a=0, b=0, past=0, solutions=0, history=0): | |||

if(past==0): | |||

past=[[True] * (len(state[0])+1) for i in range(len(state)+1)] | |||

solutions = {} | |||

history = [] | |||

if(b==len(state[0])+1): | |||

return True | |||

res=0 | |||

index=((a,b), tuple(history[-(len(state)+2):])) | |||

if index in solutions: | |||

return solutions[index] | |||

for cell in [True, False]: | |||

# either all True(c[0][0] and 1 cell) or all False (!c[0][0] and !1 cell) | |||

if (not a or not b) or len(set([((past[a][b-1] + past[a-1][b] | |||

+ past[a-1][b-1] + cell)==1), state[a-1][b-1]]))==1: | |||

history.append(cell) | |||

past[a][b] = cell | |||

res+=answer(state, a=(a+1)%(len(state)+1), | |||

b=b+(a+1)//(len(state)+1), past=past, | |||

solutions=solutions, history=history) | |||

history.pop() | |||

solutions[index]=res | |||

return res |

+ 19
- 0

find_the_access_code.py
View File
@@ -0,0 +1,19 @@ | |||

""" | |||

This one was really simple, compared to the XORFest I did before: you just | |||

get all the pairs that divide themselves with a larger number after, then do the | |||

same again. | |||

There's really nothing else to say, weird to see that as a level 3 | |||

""" | |||

def answer(l): | |||

triples = 0 | |||

pairs = [0]*len(l) | |||

for i in range(1, len(l)-1): | |||

for j in range(i): | |||

if(l[i]%l[j]==0): | |||

pairs[i]+=1 | |||

for i in range(2, len(l)): | |||

for j in range(1, i): | |||

if(l[i]%l[j]==0): | |||

triples += pairs[j] | |||

return triples |

+ 26
- 0

fuel_injection_perfection.py
View File
@@ -0,0 +1,26 @@ | |||

""" | |||

Logic is pretty much self explained there, complexity should be also fairly low, | |||

but heck was it tricky | |||

""" | |||

def answer(n): | |||

# n is given as a string so let's cast it | |||

n=int(n) | |||

res = 0 | |||

while(n!=1): | |||

# if our number is odd then let's just divide by 2 | |||

if(n%2==0): | |||

n=n/2 | |||

# now this is the tricky part and took me a bunch of googling to figure | |||

# out: instead of going the brute force O(n²) way and just trying out | |||

# every possibility in nested loops you can actually figure out if you | |||

# should add or remove: when rightmost bits are set as "111" the | |||

# preferred operation is to add, as that would push back everything as | |||

# 000s, while any other case would be faster the other way around. | |||

elif((n==3) or ((n+1)&n) > ((n-1)&(n-2))): | |||

n-=1 | |||

else: | |||

n+=1 | |||

res+=1 | |||

return res |

+ 44
- 0

ion_flux_relabeling.py
View File
@@ -0,0 +1,44 @@ | |||

""" | |||

Creating a perfect tree and going through it would be way over the top | |||

for that, so we just do a recursive search calculating on the fly the values, | |||

which results in an O(log(n)) complexity | |||

""" | |||

def find_item(item,cur,dif): | |||

#the difference between the right node and the left | |||

#node is the floor of the right node divided by 2: since we have a perfect | |||

#tree our tree have the same number of nodes on the left side and right | |||

#side; as such dividing by 2 should get you to the left one. | |||

right_node=cur-1 | |||

left_node=right_node-dif//2 | |||

#Check readme.txt | |||

if(right_node==item or left_node==item): | |||

return cur | |||

else: | |||

#Cannot be on the right side, otherwise it'd be larger than the | |||

#top left node thanks to perfect trees properties. | |||

if(item<=left_node): | |||

return find_item(item,left_node,dif//2) | |||

else: | |||

return find_item(item,right_node,dif//2) | |||

#Check readme.txt for what this function does, almost no computation there | |||

def answer(h, q): | |||

#check readme.txt | |||

if(h > 30 or h < 1): | |||

raise ValueError('Height is outside of bounds') | |||

if(len(q) > 10000 or len(q) < 1): | |||

raise ValueError('Flux converters list is outside of bounds') | |||

#Note: A perfect binary tree has 2n+1-1 nodes, where n is the height | |||

#https://xlinux.nist.gov/dads/HTML/perfectBinaryTree.html | |||

items=(2**h)-1 | |||

array=[] | |||

for i in range(len(q)): | |||

if (q[i]<items and q[i]>0): | |||

array.append(find_item(q[i],items,items-1)) | |||

else: | |||

array.append(-1) | |||

return array |

+ 35
- 0

queue_to_do.py
View File
@@ -0,0 +1,35 @@ | |||

""" | |||

This would have been fairly easy without the time limit but of course we must do | |||

some XOR regression bs to get it working. | |||

This was actually fairly simple maths once you think about it! XOR is a rotary | |||

operator when dealing with lists of following integer, ie: | |||

>>> 1 | |||

1 | |||

>>> 1^2 | |||

3 | |||

>>> 1^2^3 | |||

0 | |||

>>> 1^2^3^4 | |||

4 | |||

>>> 1^2^3^4^5 | |||

1 | |||

[...] | |||

And thus we can deduce a pattern, noted below as the xor_rotation. | |||

Thanks to that and XOR properties we can reduce the complexity of the original | |||

dumb "XOR EVERYTHING" down to an O(1) for each array, so we end up roughly | |||

having an O(length) complexity | |||

""" | |||

def xor(a, b): | |||

if(a%2==0): | |||

xor_rotation = [b, 1, b+1, 0] | |||

else: | |||

xor_rotation= [a, a^b, a-1, (a-1)^b] | |||

return xor_rotation[(b-a)%4] | |||

def answer(start, length): | |||

res=0 | |||

for i in range(0, length): | |||

res ^= xor(start+(length*i), start+(length*i)+(length-i)-1) | |||

return res |

+ 32
- 0

solar_doomsday.py
View File
@@ -0,0 +1,32 @@ | |||

#I might over-comment those solutions since it might be | |||

#reviewed one day, so bear with me, reviewers! | |||

""" | |||

Gets the biggest square below or equal to max_number | |||

I'm sure it can be optimized but as we never get over 1M | |||

it should be largely fast enough for most cpus: | |||

➜ /tmp time python2 tmp.py | |||

[1000000] | |||

python2 tmp.py 0.01s user 0.00s system 96% cpu 0.009 total | |||

The code is also more readable this way | |||

""" | |||

def get_biggest_square(max_number): | |||

n=1 | |||

while(n*n < max_number+1): | |||

n=n+1 | |||

return n-1 | |||

#Check readme.txt for an explaination of what this does. | |||

# "Heavy" computation is done in get_biggest_square | |||

def answer(area): | |||

#check readme.txt | |||

if(area > 1000000 or area < 1): | |||

raise ValueError('Area is outside of bounds') | |||

array=[] | |||

#no need to sort or do fancy pant stuff, we go from biggest to smallest in | |||

#a natural fashion this way | |||

while(area != 0): | |||

res=get_biggest_square(area) | |||

array.append(res*res) | |||

area-=res*res | |||

return array |

Loading…