Agree that this one was less fun, and I kind of hate my Python solution, which is much muddier than yours. I don't know that I have the energy to try to do this in Go.
fromcollectionsimportdefaultdictdefmain():guard_sleep_dict=calculate_guard_sleep_dict()part_one(guard_sleep_dict)part_two(guard_sleep_dict)defcalculate_guard_sleep_dict():withopen('input.txt','r')asdata:sorted_data=sorted(data)# This will work too... No extra key required
guard_sleep_dict=defaultdict(lambda:[0forxinrange(60)])current_guard=-1start_sleeping=-1forlineinsorted_data:ifline[25]=="#":current_guard=line.split()[3]elifline[25]=="a":start_sleeping=int(line[15:17])else:# "wakes up"
end_sleeping=int(line[15:17])forxinrange(start_sleeping,end_sleeping):guard_sleep_dict[current_guard][x]+=1returnguard_sleep_dictdefpart_one(guard_sleep_dict):guard=sorted(guard_sleep_dict.keys(),key=lambdag:-sum(guard_sleep_dict[g]))[0]gh=guard_sleep_dict[guard]minute=gh.index(max(gh))printint(guard[1:])*minutedefpart_two(guard_sleep_dict):guard=sorted(guard_sleep_dict.keys(),key=lambdag:-max(guard_sleep_dict[g]))[0]gh=guard_sleep_dict[guard]minute=gh.index(max(gh))printint(guard[1:])*minuteif__name__=='__main__':main()
First missunderstood the task, so I wasted my whole time before university for the wrong solution... Fortunately I had some time after the first seminar to fix my solution.
Then found out that I can just call sorted and sort the data and I don't need any extra key for this. This made solving the puzzle much easier.
Did that yesterday! Elegantly solved exactly the wrong problem, had to go back and re-read the whole page to figure out what I was doing wrong. Devastating.
Yeah it is just super frustrating... Especially if you notice then that you have to move on to do other things and will loose expensive points on your private leaderboards 🙈
Tomorrow I will read the task at least twice before I start typing.
I used the same approach, but I really like the tools you have for searching the structure. The Clojure ones work, but were much more manual, making the code a little more clunky.
never met a part of the stack I didn't like. sr. engineer at clique studios in chicago, perpetual creative hobbyist, bird friend, local gay agenda promoter. she/her. tips: https://ko-fi.com/carlymho
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
The first part made me think a little about the data structure, so I decided to create a map of the guard ID to the guard's times, where the times is a map of the minute to the number of times that the guard was asleep in that minute.
Fortunately, the date/time format is year/month/day/hour/minute, which can be sorted alphanumerically, and once that's done all sleep and wake events occur in pairs together.
Given the fixed-width, it seemed easier to substring out the bits I was looking for.
Now that I had this table, I just needed to search through it for the bits I was looking for. After trying to pull out a key/value entry from a map for the largest value, I decided to create a helper function that I could send to reduce which let me select the "largest" value, using an arbitrary function on the value in order to compare:
Later on, someone pointed out that Clojure already has the function max-key which finds the key for the largest value, but if you want to look for a non-numeric value then you need to update the entries of the map. That meant that I ended up with a lot more code, and I don't think it was as elegant.
Anyway, now that I was reading the table and had my helper function, here is the first part:
Part 2 was similar. This time I created a temporary var to hold the function that (max-finder identity) returns, so that the map function doesn't keep calling it unnecessarily:
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Indeed, it was the longest by far so far. The challenge was posted at 9pm, PST time. I started about 9:30pm. I went to bed around midnight. Still worth it :)
Timezones really matter if you're trying to get a good score (which is one reason why I'm not trying). I really feel for the Europeans who want to compete.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Yeah, it's possible for me to change the leaderboard to order based on number of stars. It still breaks ties based on who got the most recent star sooner, but it would be a little bit less demoralizing. Does that seem like a good idea?
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Also, while it may seem that it's mighty convenient that I decide to make the leaderboard more forgiving on the first day that I miss a 9PM start, that honestly wasn't why I'm thinking about this. It just seems like more fun if there's not a difference of 400 points between 1st place and last place. :)
I have been trying to learn F#, coming from C#, and I'm quite "happy" with my previous solutions. This one challenged me more, having to think 'functional'! I am not happy with how it looks, but here it is anyway!
My other attempts are on github. Critique is highly welcome!
I made a datastructure that mapped guards to days to minutes with type: Map<string,Map<string,bool array> which made it 'easy' (or so I though) to extract the data.
letrechandleguard(datastructure:Map<string,Map<string,bool[]>>)(arr:stringlist)=matcharrwith|head::tail->matchheadwith|Regex@"\[(\d+)-(\d\d)-(\d\d) (\d+):(\d+)\] (.+)"[year;month;day;hour;min;action]->letdate=newDateTime(year|>int,month|>int,day|>int,hour|>int,min|>int,0)letorigday=date.ToString"MM-dd"letnewdate=ifdate.Hour=0thendateelsenewDateTime(date.AddDays(1.0).Year,date.AddDays(1.0).Month,date.AddDays(1.0).Day)letnewday=newdate.ToString"MM-dd"matchactionwith|Regex@"Guard #(\d+)"[newguard]->ifdatastructure.ContainsKeynewguardthenifnot(datastructure.[newguard].ContainsKeynewday)thenletnewDs=letminutes:boolarray=Array.zeroCreate60letnewDay=ifhour="00"thenminutes.[min|>int]<-trueelseminutes.[0]<-truedatastructure.[newguard]|>Map.addnewdayminutesdatastructure|>Map.addnewguardnewDayhandlenewguardnewDstailelsehandlenewguarddatastructuretailelse// create new level in datastructure// new array of waking minutes, default to sleepletminutes:boolarray=Array.zeroCreate60// guard starts out as awakeletnewMap=Map.empty|>Map.addnewdayminutesletnewDs=datastructure|>Map.addnewguardnewMaphandlenewguardnewDstail|Regex@"wakes up"[]->// set time to awakedatastructure.[guard].[newday].[min|>int]<-truehandleguarddatastructuretail|Regex@"falls asleep"[]->// set all times before this to awakeletmaxMin=min|>intletminMin=tryArray.findIndexBack(funa->a)datastructure.[guard].[newday].[0..maxMin]with|:?KeyNotFoundExceptionasex->0foriin[minMin..maxMin-1]dodatastructure.[guard].[newday].[i]<-truehandleguarddatastructuretail|_->handleguarddatastructuretail|_->handleguarddatastructuretail|[]->// guard ends his day awakeMap.iter(fungdm->Map.iter(fundms->letminMin=tryArray.findIndexBack(funa->a)mswith|:?KeyNotFoundExceptionasex->0foriin[minMin..59]doms.[i]<-true)dm)datastructure// guard starts his day awakeMap.iter(fungdm->Map.iter(fundms->letminMin=tryArray.findIndex(funa->a)mswith|:?KeyNotFoundExceptionasex->0foriin[0..minMin]doms.[i]<-true)dm)datastructuredatastructure
Then Part 1
lettestinput="[1518-11-01 00:00] Guard #10 begins shift
[1518-11-01 00:05] falls asleep
[1518-11-01 00:25] wakes up
[1518-11-01 00:30] falls asleep
[1518-11-01 00:55] wakes up
[1518-11-01 23:58] Guard #99 begins shift
[1518-11-02 00:40] falls asleep
[1518-11-02 00:50] wakes up
[1518-11-03 00:05] Guard #10 begins shift
[1518-11-03 00:24] falls asleep
[1518-11-03 00:29] wakes up
[1518-11-04 00:02] Guard #99 begins shift
[1518-11-04 00:36] falls asleep
[1518-11-04 00:46] wakes up
[1518-11-05 00:03] Guard #99 begins shift
[1518-11-05 00:45] falls asleep
[1518-11-05 00:55] wakes up"letstopwatch=System.Diagnostics.Stopwatch.StartNew()letsplit=splittestinput|>Array.sort// (guard,day,minute) -> awakeletdatastruct=split|>Array.toList|>handle""Map.emptystopwatch.Stop()printfn"creating datastructure took %i milliseconds"stopwatch.ElapsedMillisecondsstopwatch.Restart()// find guard that is asleep mostletsleepyGuard=datastruct|>Map.map(funguardday->Map.map(fundaymins->Array.sumBy(funawake->ifawakethen0else1)mins)day)|>Map.map(funguardday->day|>Map.fold(funaccdaymins->acc+mins)0)|>Map.toList|>List.maxBy(fun(g,min)->min)|>fststopwatch.Stop()printfn"sleepiest guard is %s"sleepyGuardprintfn"\t calculation too %i milliseconds"stopwatch.ElapsedMillisecondsstopwatch.Restart()// find that guards most likely minute of being asleepletsleepyMinute=datastruct.[sleepyGuard]|>Map.map(fundaymins->Array.indexedmins)|>Map.toList|>List.map(funi->sndi)|>List.collect(funarr->Array.toListarr)|>List.groupBy(fun(index,awake)->index)|>List.map(funarr->sndarr)|>List.map(funa->List.map(funb->sndb)a)|>List.map(funa->a|>List.filter(funawake->notawake)|>List.length)|>List.indexed|>List.maxBy(fun(index,m)->m)|>fststopwatch.Stop()printfn"sleepiest minute of guard %s is %i"sleepyGuardsleepyMinuteprintfn"result of day 4 part 1 is: %i"((sleepyGuard|>int)*sleepyMinute)printfn"\t calculation took %i milliseconds"stopwatch.ElapsedMilliseconds
and Part 2
letpopular=datastruct|>Map.map(funguardday->Map.toListday|>List.mapsnd|>List.map(funi->Array.toListi|>List.indexed)|>List.concat//|> List.filter (fun (_,awake) -> not awake)|>List.groupBy(fun(i,_)->i)|>List.map(fun(i,l)->(i,(List.fold(funacc(_,awake)->ifawakethenaccelseacc+1)0l)))|>List.maxBy(fun(_,l)->l))|>Map.toList|>List.maxBy(fun(_,(_,t))->t)stopwatch.Stop()printfn"Guard %s spent minute %i asleep more than anyone: %i times"(fstpopular)(fst(sndpopular))(snd(sndpopular))printfn"result of day 4 part 2 is: %i"(((fstpopular)|>int)*(fst(sndpopular)))printfn"\t calculation took %i milliseconds"stopwatch.ElapsedMilliseconds
F#!
I found today the easiest thus far, but it ended up being the most verbose solution. Despite that I'm relatively pleased with it - it runs fast and I find it readable.
namespaceDay4openSystem.Text.RegularExpressionsmoduleutil=letgetContentsfileName=System.IO.File.ReadLines(fileName)|>Seq.toListtypeLogEntry=|NewGuardofSystem.DateTime*int|AsleepofSystem.DateTime|AwakeofSystem.DateTime|NonsenseletrecordRegex=@"^\[([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-3]{2}):([0-9]{2})] (.*)$"let(|FirstRegexGroup|_|)patterninput=letm=Regex.Match(input,pattern)if(m.Success)thenSomem.Groups.[1].ValueelseNoneletsortLoglog=List.sortBy(funel->matchelwith|LogEntry.Asleepdt->dt|LogEntry.Awakedt->dt|LogEntry.NewGuard(dt,_)->dt|LogEntry.Nonsense->newSystem.DateTime())logletscrapeLoginputs=List.map(funlogEntry->letm=Regex.Match(logEntry,recordRegex)ifm.Successthenletdt=newSystem.DateTime((m.Groups.[1].Value|>System.Convert.ToInt32),(m.Groups.[2].Value|>System.Convert.ToInt32),(m.Groups.[3].Value|>System.Convert.ToInt32),(m.Groups.[4].Value|>System.Convert.ToInt32),(m.Groups.[5].Value|>System.Convert.ToInt32),0)matchm.Groups.[6].Valuewith|"falls asleep"->LogEntry.Asleep(dt)|"wakes up"->LogEntry.Awake(dt)|FirstRegexGroup"Guard #([0-9]+) begins shift"badge->LogEntry.NewGuard(dt,System.Convert.ToInt32badge)|_->LogEntry.NonsenseelseLogEntry.Nonsense)inputs|>sortLogletfirst(a,_,_)=aletsecond(_,b,_)=bletthird(_,_,c)=ctypeMinuteCounts=Map<int,int>letgetHighestminuteCounts=Map.fold(funskv->ifv>sndsthen(k,v)elses)(0,0)minuteCountstypeSingleEntry={TotalAsleep:intMinuteCounts:MinuteCounts}typeSleepLog=Map<int,SingleEntry>lettickMinute(mcs:MinuteCounts)m=match(mcs.TryFindm)with|Somex->Map.addm(x+1)mcs|None->Map.addm1mcsletaddRangeToLogsleepwakesleepLogEntry=lettimeAsleep=wake-sleepleteachMinute=[foriinsleep..wake-1doyieldi]{TotalAsleep=sleepLogEntry.TotalAsleep+timeAsleep;MinuteCounts=List.foldtickMinutesleepLogEntry.MinuteCountseachMinute}letprocessLoglog=List.fold(funsel->matchelwith|LogEntry.NewGuard(_,badge)->(Somebadge,seconds,thirds)|LogEntry.Awakedt->match(firsts)with|Somebadge->match(seconds)with|SomesleepTime->letmap:SleepLog=thirdsletnewMap=matchmap.TryFindbadgewith|Somex->Map.addbadge(addRangeToLogsleepTimedt.Minutex)map|None->Map.addbadge(addRangeToLogsleepTimedt.Minute{TotalAsleep=0;MinuteCounts=(newMap<int,int>(Seq.empty))})map(firsts,None,newMap)|None->s// This branch indicates an error in the input - there was no "time asleep" stored for a "wakes up" entry|None->s// This branch indicates an error in the input - there was no active badge for a "wakes up" entry|LogEntry.Asleepdt->(firsts,Somedt.Minute,thirds)|LogEntry.Nonsense->s)(None,None,newMap<int,SingleEntry>(Seq.empty))// Active Badge, Minute Fell Asleep, SleepLoglogletgetSleepLogfileName=getContentsfileName|>scrapeLog|>processLog|>thirdmodulepart1=openutilletgetLongestAsleepsleepLog=sleepLog|>Map.fold(funskv->ifv.TotalAsleep>firststhen(v.TotalAsleep,k,getHighestv.MinuteCounts)elses)(0,0,(0,0))letexecutefileName=letres=getSleepLogfileName|>getLongestAsleep(secondres)*(fst(thirdres))modulepart2=openutilletgetMostFrequentAsleepsleepLog=sleepLog|>Map.fold(funsk(v:SingleEntry)->leth=getHighestv.MinuteCountsifsndh>=snd(snds)then(k,h)elses)(0,(0,0))letexecutefileName=letres=getSleepLogfileName|>getMostFrequentAsleep(fstres)*(fst(sndres))
See Neil Gall's wonderful post for a more functional-paradigm approach. Kotlin not being pure-functional makes me lean into the procedural style more often than I'd like.
Anyway...
Part 1
I didn't read the problem properly. Some key things I missed:
The input data is random.
We only care about minutes.
There's only ever one guard on duty (not a stack of sleeping and awake guards).
Fortunately, once I knew what I was supposed to be recording, it was just a simple matter of adding sleeping events to guards.
So, the thing that threw me here is that some guards don't fall asleep, leading .maxBy to return null. Otherwise, this was a fairly simple modification of the first part, just aggregating at different points. At this point, I'm wondering if Eric Wastl is experimenting with reversing the difficulties this year. This is two in a row where the heavy lifting gets done in part 1. We'll see when tomorrow's drops!
My Python solution, which I'm less than jazzed about. And I definitely have no interest in redoing it in Go today. I do not have the drive or energy. /shrug
const{readFile}=require('./reader');const{SleepingSchedule,Guard,buildGuards}=require('./04-common');constfindZonkedGuard=guards=>{return[...guards.values()].reduce((zonkedGuard,guard)=>{if(guard.maxMinutesSlept>zonkedGuard.maxMinutesSlept){zonkedGuard=guard;}returnzonkedGuard;});};constfindMostAsleepMinute=guard=>{constminutesCount=Array.from({length:60},m=>0);for(leti=0;i<60;i++){for(letscheduleofguard.schedules){minutesCount[i]+=+(schedule.minutesSlept.indexOf(i)>-1);}}constmostAsleepMinuteCount=Math.max(...minutesCount);returnminutesCount.indexOf(mostAsleepMinuteCount);};(async()=>{constlines=awaitreadFile('04-input.txt');lines.sort();constguards=buildGuards(lines);constzonkedGuard=findZonkedGuard(guards);constmostAsleepMinute=findMostAsleepMinute(zonkedGuard);constsolution=+zonkedGuard.id*mostAsleepMinute;console.log(`The ID of the guard multiplied by the minute is ${solution}`);})();
04b.js
const{readFile}=require('./reader');const{SleepingSchedule,Guard,buildGuards}=require('./04-common');constfindMostAsleepMinute=guard=>{constminutesCount=Array.from({length:60},m=>0);for(leti=0;i<60;i++){for(letscheduleofguard.schedules){minutesCount[i]+=+(schedule.minutesSlept.indexOf(i)>-1);}}constmostAsleepMinuteCount=Math.max(...minutesCount);returnminutesCount.indexOf(mostAsleepMinuteCount);};constfindMostFrequentlySleptMinute=guards=>{constminutesMostSlept=[];// Finding the guard which slept the most minutes for every minutefor(leti=0;i<60;i++){constguardsWhichSleptThisMinute=newMap();for(letguardofguards.values()){letminutesThisGuardSlept=0;for(letscheduleofguard.schedules){minutesThisGuardSlept+=+(schedule.minutesSlept.indexOf(i)>-1);}guardsWhichSleptThisMinute.set(guard.id,minutesThisGuardSlept);}letzonkedGuardId;letmaxMinutesSlept=0;for(let[guardId,minutes]ofguardsWhichSleptThisMinute.entries()){if(minutes>maxMinutesSlept){maxMinutesSlept=minutes;zonkedGuardId=guardId;}}minutesMostSlept[i]={zonkedGuardId,maxMinutesSlept};}// Finding the guard which slept the most minutes totalletzonkedGuardIdTotal;letmaxMinutesSleptTotal=0;letminuteMostSleptTotal=-1;for(leti=0;i<60;i++){const{zonkedGuardId,maxMinutesSlept}=minutesMostSlept[i];if(maxMinutesSlept>maxMinutesSleptTotal){maxMinutesSleptTotal=maxMinutesSlept;zonkedGuardIdTotal=zonkedGuardId;minuteMostSleptTotal=i;}}return{zonkedGuardIdTotal,minuteMostSleptTotal}}(async()=>{constlines=awaitreadFile('04-input.txt');lines.sort();constguards=buildGuards(lines);const{zonkedGuardIdTotal,minuteMostSleptTotal}=findMostFrequentlySleptMinute(guards);constsolution=+zonkedGuardIdTotal*minuteMostSleptTotal;console.log(`The ID of the guard multiplied by the minute is ${solution}`);})();
Had a particularly long day at work, so got to the problem late and only just now finished. I'm with all of you that this one was less fun...I haven't refactored since I don't have much brain left for today, but here's my solution for now!
#!/usr/bin/env python
importoperatorimportrefromcollectionsimportdefaultdictRECORD_PATTERN=re.compile('\[.* \d\d:(\d\d)\] .*')GUARD_ID_PATTERN=re.compile('.* Guard #(\d+) begins shift')if__name__=='__main__':withopen('input.txt')asguard_records_file:guard_records=guard_records_file.read().splitlines()guard_records.sort()minute_data=defaultdict(lambda:defaultdict(int))guard_data=defaultdict(lambda:defaultdict(int))forguard_recordinguard_records:minute=int(RECORD_PATTERN.match(guard_record).group(1))if'begins shift'inguard_record:just_started_shift=Truecurrent_guard=int(GUARD_ID_PATTERN.match(guard_record).group(1))elif'wakes up'inguard_record:minutes_asleep=minute-fell_asleep_minuteforsleep_minuteinrange(fell_asleep_minute,minute):guard_data[current_guard][sleep_minute]+=1minute_data[sleep_minute][current_guard]+=1elif'falls asleep'inguard_record:fell_asleep_minute=minute# Part 1
guard_who_slept_most=max(guard_data,key=lambdakey:sum(guard_data[key].values()))sleepiest_minute_for_guard=max(minute_data,key=lambdakey:minute_data[key][guard_who_slept_most])print(guard_who_slept_most*sleepiest_minute_for_guard)# Part 2
guard_with_sleepiest_minute=max(guard_data,key=lambdakey:max(guard_data[key].values()))sleepiest_minute_for_guard=max(guard_data[guard_with_sleepiest_minute],key=lambdakey:guard_data[guard_with_sleepiest_minute][key])print(guard_with_sleepiest_minute*sleepiest_minute_for_guard)
Didn't really have much time to do the challenges today, but yeah, whenever you did the first three days it's hard to skip one.
So here are my solutions in Elixir. Some function returns are a bit messy, but it does what it needs to do. The basic idea is that all lines are first parsed into a map with guards, where each guard is the key for another map containing the minutes and the number of times the guard was asleep during that minute.
Having that basic map makes the calculations for both strategies relatively easy.
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Done! Completed my solution with just minutes to spare before the next day goes live! I thought I would like this challenge. I did not. It was fine, for the most part. But I spent at least fifteen minutes wondering why my solution wasn't working before I realized the #!@%#@!$! LOGS WERE NOT IN CHRONOLOGICAL ORDER.
Let me tell you: I was not calm in that moment.
After I used VS Code's built-in sorting functionality to pre-sort the input text, it was mostly smooth sailing. However, rust gurus, before I sorted my input, every time I ran cargo run, the output would be a different answer, even with no changes to the code. Anybody know why that might be? I would expect a static input file and static code to make the same result every time.
Anyways.
Part 1
usestd::collections::HashMap;/// Day 4: Repose Record/// /// Track the sleeping times of various security guards// Part 1: Find the guard who slept most and their most-slept minute/// The security team (made up of a bunch of Guards) that we're monitoringstructSecurityTeam{guards:HashMap<usize,Guard>,}implSecurityTeam{pubfnnew()->Self{Self{guards:HashMap::new()}}/// Loads in a log-file-like schedule from text/// /// Entries have the form '[YYYY-MM-DD HH:MM] message'/// Possible messages are 'Guard #<id> begins shift'/// 'falls asleep'/// 'wakes up'/// Assumes that the logs are in chronological order (WHICH IS SANE)pubfnload_schedule(&mutself,text:&str){letmutcurrent_guard=0;letmutcurrent_state="";letmutsleep_start=0;forlineintext.lines(){// Yeah I'm hard-coding the minutes location, DON'T JUDGE MEletminute:usize=line[15..17].parse().expect("Minutes weren't a number");letaction=&line[19..];ifaction.contains("Guard"){// New guard starting shift.letid=action.trim_matches(|c:char|!c.is_numeric()).parse().expect("No guard ID");self.guards.entry(id).or_insert(Guard::new(id));current_guard=id;current_state="awake";}elseifaction.contains("falls asleep"){// Ignores double-falls asleepifcurrent_state=="awake"{current_state="asleep";sleep_start=minute;}}elseifaction.contains("wakes up"){// Ignores double-wakesifcurrent_state=="asleep"{self.guards.get_mut(¤t_guard).unwrap().track_sleep(sleep_start,minute);current_state="awake";}}}}/// Returns the guard with the overall most minutes asleeppubfnsleepiest_guard(&self)->&Guard{self.guards.values().max_by_key(|guard|guard.total_minutes_asleep()).expect("No guards on team")}}/// A security guard. He keeps track of his own sleep times (what a great person)!structGuard{id:usize,sleep_minutes:HashMap<usize,usize>,}implGuard{pubfnnew(id:usize)->Self{Self{id,sleep_minutes:HashMap::new()}}pubfnid(&self)->usize{self.id}/// Returns the minute in which this guard most commonly slept/// /// Accounts for the possibility that this guard doesn't suck at their/// job and stays awake the whole time.pubfnsleepiest_minute(&self)->usize{*self.sleep_minutes.iter().max_by_key(|(_id,minutes)|**minutes).unwrap_or((&0,&0)).0}/// Sums up this guards total sleeping timepubfntotal_minutes_asleep(&self)->usize{self.sleep_minutes.values().sum()}/// Logs in a length of time where the guard was asleeppubfntrack_sleep(&mutself,asleep:usize,awake:usize){forminuteinasleep..awake{*self.sleep_minutes.entry(minute).or_insert(0)+=1}}}/// Part 1 asks for the ID of the guard who slept the most multiplied by/// the minute they slept mostpubfnpart1(text:&str)->usize{letmutguards=SecurityTeam::new();guards.load_schedule(text);letsleepy=guards.sleepiest_guard();sleepy.id()*sleepy.sleepiest_minute()}
Part 2
Part 2 went pretty quick now that I've got all this infrastructure in place.
// Part 2implSecurityTeam{/// Returns the guard that fell asleep the most on the same minutepubfnmost_consistent_sleeper(&self)->&Guard{self.guards.values().max_by_key(|guard|guard.sleep_on(guard.sleepiest_minute())).expect("No guards on team")}}implGuard{/// Returns the amount of times this guard slept on a given minutepubfnsleep_on(&self,minute:usize)->usize{*self.sleep_minutes.get(&minute).unwrap_or(&0)}}/// Part two asks for the ID of the guard who slept the most on a single/// minute multiplied by that minutepubfnpart2(text:&str)->usize{letmutguards=SecurityTeam::new();guards.load_schedule(text);letconsistent_guard=guards.most_consistent_sleeper();consistent_guard.id()*consistent_guard.sleepiest_minute()}
I'm a Staff Software Engineer at Cisco. I specialize in Python and Go, building functional, practical, and maintainable web systems leveraging Kubernetes and the cloud. Blog opinions are my own.
Python!
May refactor later, but this problem was a little less fun for me than other ones so far, to be honest.
part 1
part 2
Agree that this one was less fun, and I kind of hate my Python solution, which is much muddier than yours. I don't know that I have the energy to try to do this in Go.
Yeah, I like the algorithm-y ones, this one was more string parsing I think!
It was a complicated description though, I had a lot of problems parsing what I was supposed to be finding.
TOTALLY -- I only figured it out once I looked at the input. The graph actually totally threw me off.
First missunderstood the task, so I wasted my whole time before university for the wrong solution... Fortunately I had some time after the first seminar to fix my solution.
Then found out that I can just call sorted and sort the data and I don't need any extra key for this. This made solving the puzzle much easier.
Did that yesterday! Elegantly solved exactly the wrong problem, had to go back and re-read the whole page to figure out what I was doing wrong. Devastating.
Yeah it is just super frustrating... Especially if you notice then that you have to move on to do other things and will loose expensive points on your private leaderboards 🙈
Tomorrow I will read the task at least twice before I start typing.
Damn life, getting in the way of AoC. The very nerve.
I used the same approach, but I really like the tools you have for searching the structure. The Clojure ones work, but were much more manual, making the code a little more clunky.
PHP
Had to brush up on the difference between usort, uasort, and uksort for this one, ahaha.
Part 1:
Part 2:
UKSort works the same, but is just more polite and has a slight accent, right? :]
The first part made me think a little about the data structure, so I decided to create a map of the guard ID to the guard's times, where the times is a map of the minute to the number of times that the guard was asleep in that minute.
Fortunately, the date/time format is year/month/day/hour/minute, which can be sorted alphanumerically, and once that's done all sleep and wake events occur in pairs together.
Given the fixed-width, it seemed easier to substring out the bits I was looking for.
Now that I had this table, I just needed to search through it for the bits I was looking for. After trying to pull out a key/value entry from a map for the largest value, I decided to create a helper function that I could send to
reducewhich let me select the "largest" value, using an arbitrary function on the value in order to compare:Later on, someone pointed out that Clojure already has the function
max-keywhich finds the key for the largest value, but if you want to look for a non-numeric value then you need to update the entries of the map. That meant that I ended up with a lot more code, and I don't think it was as elegant.Anyway, now that I was reading the table and had my helper function, here is the first part:
Part 2 was similar. This time I created a temporary var to hold the function that
(max-finder identity)returns, so that themapfunction doesn't keep calling it unnecessarily:I like how having the correct data structure meant that the final calculations could be encapsulated in just a couple of lines.
Anybody feel like this today?
YES I had to read the question like 18 times.
Indeed, it was the longest by far so far. The challenge was posted at 9pm, PST time. I started about 9:30pm. I went to bed around midnight. Still worth it :)
Timezones really matter if you're trying to get a good score (which is one reason why I'm not trying). I really feel for the Europeans who want to compete.
Yeah, it's possible for me to change the leaderboard to order based on number of stars. It still breaks ties based on who got the most recent star sooner, but it would be a little bit less demoralizing. Does that seem like a good idea?
Also, while it may seem that it's mighty convenient that I decide to make the leaderboard more forgiving on the first day that I miss a 9PM start, that honestly wasn't why I'm thinking about this. It just seems like more fun if there's not a difference of 400 points between 1st place and last place. :)
I have been trying to learn F#, coming from C#, and I'm quite "happy" with my previous solutions. This one challenged me more, having to think 'functional'! I am not happy with how it looks, but here it is anyway!
My other attempts are on github. Critique is highly welcome!
I made a datastructure that mapped guards to days to minutes with type:
Map<string,Map<string,bool array>which made it 'easy' (or so I though) to extract the data.Then Part 1
and Part 2
F#!
I found today the easiest thus far, but it ended up being the most verbose solution. Despite that I'm relatively pleased with it - it runs fast and I find it readable.
Non-functional-paradigm kotlin solution
See Neil Gall's wonderful post for a more functional-paradigm approach. Kotlin not being pure-functional makes me lean into the procedural style more often than I'd like.
Anyway...
Part 1
I didn't read the problem properly. Some key things I missed:
Fortunately, once I knew what I was supposed to be recording, it was just a simple matter of adding sleeping events to guards.
Part 2
So, the thing that threw me here is that some guards don't fall asleep, leading
.maxByto returnnull. Otherwise, this was a fairly simple modification of the first part, just aggregating at different points. At this point, I'm wondering if Eric Wastl is experimenting with reversing the difficulties this year. This is two in a row where the heavy lifting gets done in part 1. We'll see when tomorrow's drops!and since I got bored waiting for some scans to complete, here's what my schedule looks like!
I should probably just pick the days that don't have guards!
I like your use of
-ensuffixes to denote verbs. Makes a lot of sense, actually, easy to glean intent.Haha, I was tired and started with
awakenand I thoughtasleepenwas really hilarious.Of course once I realized that there was a distinction between going on duty and waking up from a sleep, I was honor-bound to add
ondutyenMy Python solution, which I'm less than jazzed about. And I definitely have no interest in redoing it in Go today. I do not have the drive or energy. /shrug
JavaScript solution
reader.js
04-common.js
04a.js
04b.js
Had a particularly long day at work, so got to the problem late and only just now finished. I'm with all of you that this one was less fun...I haven't refactored since I don't have much brain left for today, but here's my solution for now!
Didn't really have much time to do the challenges today, but yeah, whenever you did the first three days it's hard to skip one.
So here are my solutions in Elixir. Some function returns are a bit messy, but it does what it needs to do. The basic idea is that all lines are first parsed into a map with guards, where each guard is the key for another map containing the minutes and the number of times the guard was asleep during that minute.
Having that basic map makes the calculations for both strategies relatively easy.
Common:
Part one:
Part two:
Done! Completed my solution with just minutes to spare before the next day goes live! I thought I would like this challenge. I did not. It was fine, for the most part. But I spent at least fifteen minutes wondering why my solution wasn't working before I realized the #!@%#@!$! LOGS WERE NOT IN CHRONOLOGICAL ORDER.
Let me tell you: I was not calm in that moment.
After I used VS Code's built-in sorting functionality to pre-sort the input text, it was mostly smooth sailing. However, rust gurus, before I sorted my input, every time I ran
cargo run, the output would be a different answer, even with no changes to the code. Anybody know why that might be? I would expect a static input file and static code to make the same result every time.Anyways.
Part 1
Part 2
Part 2 went pretty quick now that I've got all this infrastructure in place.
Someone needs to talk to these elves about the awful ways they're recording and storing data.
Data integrity is important people. Especially at this kind of scale!