Automating google hashcode's submission system with selenium
technologyยท@howoยท
0.000 HBDAutomating google hashcode's submission system with selenium
Last thursday it was the google hashcode, it's a huge competition around the world organized by google https://hashcode.withgoogle.com/ it's a fun hackathon that lasts 2 hours for the first round. This year the subject was automated vehicule management : You can find it in full here : https://drive.google.com/file/d/16brGfFeBPRYYVh1oD8ADgh333aGqEtAH/view given a set of vehicules, a map (in the form of a grid) of and a set of rides (I want to go from position x;y to position x1;y1), you need to assign the vehicules to pickup the correct rides so that when they finish a ride, there is another one nearby. In order to finish everything as fast as possible. There are other parameters but we won't discuss them now are they are irrelevant. So in the end you get several input files containing various scenarios, with a map bigger and bigger and more and more rides and vehicles. Your job is to run your algorithm, allocate the correct rides and generate an output file which looks like this : 0 1 2 1 3 here we allocate the rides 1 and 2 to the vehicle 0 and the ride 3 to the vehicle 1. Then submit the files to the juding system which will give us points depending on how well it went. In this competition it's important to draw "first blood", aka having a result and fast and then build on it. So our first solution was to allocate all of the rides to one vehicle, which did poorly obviously, then the second dumb solution was to randomly allocate rides to the vehicules. In terms of code it looks like this : (python) ``` vehicles_nb = data['vehicles'].__len__() # total number of vehicles for ride in data['rides']: # iterate over the rides id = randint(0, vehicles_nb-1) # get a random vehicle data['vehicles'][id]['rides'].append(ride['id']) # add the ride id to the vehicle rides ``` Pretty easy right ? Note that if you submit it to the judging system, only the best score is kept, so after running it a few times we realized that our score was increasing (as we randomly do better than our best score). But submitting takes a lot of time because there is a lot of steps : - Click on the Authenticate button - Write username - Click next - Write password - Click login - click on "submit new submission" - click on upload - select the output file - click on send - repeat for the 5 files - click on submit to get the notation So it takes a good 40-50 seconds to submit the output files once. Then came a wild idea : What if I automated that whole process and along our efforts to come up with "real" algorithms, randomly try to get better results by chance ? So I started automating it using https://www.seleniumhq.org/ I wrote a guide on it a few months ago (which has its formating broken but the information is here : https://steemit.com/programming/@howo/web-browser-automation-in-python-with-selenium) I'll show you a gif of the result beforehand so you get some idea of what's happening in the code :  First we create the driver and open the hashcode page : ``` driver = webdriver.Chrome('./chromedriver') driver.get('https://hashcodejudge.withgoogle.com/#/rounds/4868850726207488/submissions/') ``` We are asked to login and as soon as we click on authenticate, a popup opens and they ask us to do so. Hence : ``` driver.find_element_by_class_name("md-primary").click() # click on authenticate time.sleep(0.5) # wait for the window to appear driver.switch_to.window(driver.window_handles[1]) # switch the focus to the new windows driver.find_element_by_id("identifierId").send_keys("username") # send the username driver.find_element_by_id("identifierNext").click() # click on the "next" button time.sleep(1) # wait for the window to proceed driver.find_element_by_name("password").send_keys("password") # send the password time.sleep(0.5) driver.find_element_by_id("passwordNext").click() # confirms (this closes the popup) time.sleep(1) driver.switch_to.window(driver.window_handles[0]) # Switch back to the main page ``` We have a lot of sleeps because if we go too fast the window won't fnd the next id or name. And this was coded during a hackathon so no time to write a clean "wait until you find this id/name" We switch the focus of the driver on the new window because otherwise he won't be able to interact with the content of the popup. Ok so now we are authenticated, we can submit the files > driver.find_element_by_class_name("md-primary").click() # Click on submit > time.sleep(0.5) This creates a popup with 5 buttons, <center> </center> the issue with them is that we don't have an unique id : ``` <button class="md-raised md-primary md-button md-ink-ripple" type="button" ng-transclude="" ng-click="ctrl.onUploadClick($event)" ng-disabled="!ctrl.ready || ctrl.fileName" aria-label="Upload">Upload file<div class="md-ripple-container" style=""></div></button> ``` Luckily all the buttons have this custom css : > aria-label="Upload" So we can use selenium to get a list of all the "upload" buttons : > driver.find_elements_by_css_selector('[aria-label="Upload"]') With that list and we can iterate over the 5 buttons : ``` for id, elem in enumerate(driver.find_elements_by_css_selector('[aria-label="Upload"]')) : # We iterate over the 5 upload buttons โ elem.click() โ time.sleep(0.5) ``` Then there is a file selection dialog, which is outside of the driver as it's an os window. We can't control it via selenium. We'll use pyautogui to the rescue ! (I also wrote an article about it : https://steemit.com/python/@howo/how-to-control-the-mouse-and-keyboard-with-python-for-automation) The upload windows pops in focus so we just have to hit a few good keys to select the right files. ``` \# I know the ifs are ugly but this is hackathon code, no time for pretty if (id == 0): # First time the dialog opens, we select the "output" folder pyautogui.press('down') pyautogui.press('enter') time.sleep(0.5) if (id == 2): # select the file b pyautogui.press('down') time.sleep(0.5) elif (id == 3): # select the file c pyautogui.press('down') pyautogui.press('down') time.sleep(0.2) elif (id == 4): # select the file d pyautogui.press('down') pyautogui.press('down') pyautogui.press('down') elif (id == 5): # select the file e pyautogui.press('down') pyautogui.press('down') pyautogui.press('down') pyautogui.press('down') pyautogui.press('enter') \# click on the submit button driver.find_element_by_css_selector('[ng-click="submissionsCtrl.createSubmission()"]').click() ``` And bam ! it worked. I let it run for the duration of the hackathon that was left flooding our team with random results and infuriating my teammates who could not see the results of their own submissions. At some point the randomness converged to a "maximum" and we did not manage to get past 12M points with that method (the winners of the hackathon got 45M points). You can find the complete source file for this on my github : https://github.com/drov0/hashcode-auto-submit
๐ trinemily, suesa, howo, joselync0, jobi, finkwoct, alexs1320, ipaulo, jacobts, saqibnazir, sachincool, reggaemuffin, newhope, makerhacks, smartdeveloper, ipkiss, christianity101, unsw, abcbullion, saych, bendelgreco, khayziljoy, reedracer, trumpman, steemstem, anarchyhasnogods, justtryme90, borislavzlatanov, thevenusproject, mobbs, the-devil, foundation, himal, lamouthe, nitesh9, rachelsmantra, kerriknox, gra, rjbauer85, kryzsec, amavi, dna-replication, dber, curie, gentleshaid, locikll, kenadis, mountainwashere, anwenbaumeister, hadji, hendrikdegrote, fredrikaa, dysfunctional, horpey, grandpere, timsaid, jamhuery, alexander.alexis, leczy, ovij, somethingburger, ertwro, kushed, de-stem, pharesim, velourex, steemedia, cotidiana, teofilex11, bp423, dreamien, steem-id, dirty.hera, cebymaster, lemouth, robotics101, markangeltrueman, keshawn, aboutyourbiz, howtostartablog, slickhustler007, justdentist, spectrums, chimtivers96, zipporah, pacokam8, oscarcc89, funkylove, scorpil, arnavlavan, wanderingdanish, bobdos, jaquevital, deutsch-boost, lenin-mccarthy, skycae, benleemusic, coloringiship, bennettitalia, mcfarhat, shaff.aff, nwjordan, ackza, steemstem-bot, louishugo, tdre, jjay, scienceangel, drifter1, yazdani33, seoripon,