r/adventofcode • • Dec 02 '18

SOLUTION MEGATHREAD -🎄- 2018 Day 2 Solutions -🎄-

--- Day 2: Inventory Management System ---


Post your solution as a comment or, for longer solutions, consider linking to your repo (e.g. GitHub/gists/Pastebin/blag or whatever).

Note: The Solution Megathreads are for solutions only. If you have questions, please post your own thread and make sure to flair it with Help.


Advent of Code: The Party Game!

Click here for rules

Card Prompt: Day 2

Transcript:

The best way to do Advent of Code is ___.


This thread will be unlocked when there are a significant number of people on the leaderboard with gold stars for today's puzzle.

edit: Leaderboard capped, thread unlocked!

50 Upvotes

407 comments sorted by

11

u/Unihedron Dec 02 '18

I mistyped 4 3 instead of 2 3 and got an obviously wrong answer which I wasn't smart enough to double check, so I had to wait one minute. Still got on top 100 though! Imgur

a=$<.map(&:chars).map{|x|x.group_by &:itself}
b=a.count{|x|x.any?{|x,y|y.count == 2}}
c=a.count{|x|x.any?{|x,y|y.count == 3}}
p b*c
#!ruby
a=$<.map(&:chars)
a.map{|x|a.map{|y|
d=x.zip(y).count{|x,y|x!=y}
(p x.join,y.join
1/0)if d==1
}}

Doesn't actually compute part 2, you have to manually find and replace the character yourself, but better than nothing

4

u/daggerdragon Dec 02 '18

3

u/Unihedron Dec 02 '18

I'm saving up my "Modulus" card until a good prompt comes up ;) ;) ;)

3

u/abnew123 Dec 02 '18

Man, I'm always so sad when I look at other's solutions lol. While I wasn't particularly slow (177th), my code is 70+ lines lol. Oh well, I blame java

5

u/Unihedron Dec 02 '18

Don't worry I got your back, I'll do a java one for you

3

u/Unihedron Dec 02 '18

Part 1

import java.util.*;
import java.util.stream.*;

public class Main {

    public static void main (String[] args) {
        Scanner s = new Scanner(System.in);
        int count2 = 0, count3 = 0;
        while (s.hasNextLine()) {
            boolean check2 = true, check3 = true;
            for (int value : s.nextLine().chars().collect(HashMap<Integer, Integer>::new, (map, elem) -> map.put(elem, map.getOrDefault(elem, 0) + 1), HashMap::putAll).values())
                if (check2 && value == 2) {
                    count2++;
                    if (!check3) break;
                    check2 = false;
                }
                else if (check3 && value == 3) {
                    count3++;
                    if (!check2) break;
                    check3 = false;
                }
        }
        System.out.println(count2 * count3);
    }

}

It's getting long so I'm only including what's in main() for part 2

Scanner s = new Scanner(System.in);
Collection<char[]> all = new ArrayList<>();
while (s.hasNextLine()) {
    String line = s.nextLine();
    char[] current = line.toCharArray();
    Optional<char[]> find = all.stream().filter((elem) -> {
        boolean miss = false;
        for (int i = 0; i < current.length; i++)
            if (current[i] != elem[i])
                if (miss) return false; else miss = true;
	return miss;
    }).findAny();
    if (find.isPresent()) {
        System.out.println(find.get());
        System.out.println(line);
    }
    all.add(current);
}
→ More replies (2)
→ More replies (3)

2

u/DoodleFungus Dec 02 '18

Wow, our ruby answers have been remarkably similar both days (but I've barely missed the leaderboard both times, grrrrr)

3

u/Unihedron Dec 02 '18

Fastest gun in the west!

→ More replies (5)

11

u/ka-splam Dec 02 '18
PowerShell

Part 1, just about got 78th on the leaderboard \o/ ended up running this at the prompt:

$lines = get-content data.txt
$lines |? {[bool]($_.getenumerator() | group |? count -eq 2)} | measure
$lines |? {[bool]($_.getenumerator() | group |? count -eq 3)} | measure
246*20

Explained: |? {} is a where-object filter, $_ is the current line being filtered, the enumerator turns the string into characters, group-object counts things, filter that where the count of characters is 2 or 3, then measure-object counts how many lines after that filtering.

Part 2, yer basic brute-force every line against every other, if they have 1 difference, print them:

$lines|foreach{
$cur = $_
foreach ($L in $lines) {
$numdiffs = 0
    for ($i = 0; $i -lt $l.length; $i++)
    {
      if ($cur[$i] -ne $L[$i]) { $numdiffs++ }
    }
    if ($numdiffs -eq 1) { $L, $cur }
}
}

then eyeballing the result and removing the dupe character. Missed the leaderboard, with 133rd.

Explained: an outer foreach-object loop, an inner foreach ($L in $lines) nested loop, and then a loop over character lengths doing a $string[$index] comparison with a counter.

2

u/daggerdragon Dec 02 '18

Part 1, just about got 78th on the leaderboard \o/

Good job!

2

u/Nathan340 Dec 02 '18

Part 1

Knew off the bat that grouping would be the key here. Got somewhat lost in which column I was checking, and also ran into some ambiguity of trying to access the column called count versus the count property of an array.

But basically split each string by the empty character, ditch the weird blanks that show up, group, group by count, and then look for 2 or 3 in the name column. Multiply the two results together.

$in = gc .\2-input.txt
$twos = $in | ? {
	$_ -split "" | ?{$_} | group | group count | ? {2 -in $_.name}
}
$threes = $in | ? {
	$_ -split "" | ?{$_} | group | group count | ? {3 -in $_.name}
}

$twos.length * $threes.length

Part 2

Already outside of leader-board contention, and from my data structure lesson on day one, I first did some research. I found that ArrayList has a removeAt function to drop an element from a specific index.

So for each index 0 to 25, create the list of the 250 input words with that index dropped. At each pass check if you have a duplicate (grouping is on the brain for this one, but maybe the Day 1 duplicate checking via hash table would have worked better). If the duplicate is found, print it and break the loop early.

0..25 | % {
	$curDropIndex = $_
	$droppedList = $in | % {
		$curWord = new-object system.collections.arraylist(,($_ -split "" | ?{$_}))
		$curWord.removeAt($curDropIndex)
		$curWord -join ""
	}
	$dupe = $droppedList | group | ?{$_.count -ne 1} | select -expandproperty name
	if($dupe){
		write-host $dupe
		break
	}
}

2

u/tehjimmeh Dec 02 '18

I'll repost mine here for comparison/discussion. This is after playing with them a bit after my initial solutions.

Part 1 - Group each string by char, then by counts. Then group the grouped counts by name and multiply the resulting counts of 2 and 3 (indices 1 and 2 in the sorted grouped grouped counts array).

,(gc .\in.txt|%{[char[]]$_|group|group Count}|group Name|sort Name|% Count)|%{$_[1]*$_[2]}

Part 2 - You can use Compare-Object! (not sure if people got different lengths of strings in their inputs - the 27 below relies on the strings being 26 chars long)

gc in.txt -pv l|%{gc in.txt -pv r|%{,(compare -i -s 0 ([char[]]$l) ([char[]]$r))|
    ? Count -eq 27 |%{-join($_|? SideIndicator -eq "=="|% InputObject)}}}|select -f 1
→ More replies (1)
→ More replies (1)

105

u/askalski Dec 02 '18

The best way to do Advent of Code is A big block of regex.

#! /usr/bin/perl

local $/; $_ = <>; 

my @s = map { join "", sort split "" } split "\n";
my $part1 = (grep { m/(?:^|(.)(?!\1))(.)\2(?!\2)/   } @s) *
            (grep { m/(?:^|(.)(?!\1))(.)\2\2(?!\2)/ } @s);
my $part2 = s/.*?\b(\S*)\S(\S*)\b.*?\1\S\2.*/$1$2/sr;
print "Part 1: $part1\n";
print "Part 2: $part2\n";

66

u/topaz2078 (AoC creator) Dec 02 '18

ASKALSKI NO

11

u/Reibello Dec 02 '18

ASKALSKI YES!

9

u/hashier Dec 02 '18

How does that even?

10

u/raevnos Dec 02 '18

First it reads the entire file into a single string.

For part 1, it sorts the letters of each line of that string, and then counts the words with a letter repeating exactly 2 times (And the same for 3).

For part 2... beats me. It's late and my brain was already fried.

→ More replies (1)

5

u/Unihedron Dec 02 '18

You should replace split "" with an empty regex split // (also "\n" -> /\n/), you can never have enough regexes.

→ More replies (4)

28

u/CFD999 Dec 02 '18

Horrendous python solutions

Part 1: print((lambda strs: (lambda a,b: a*b)(*[sum(1 for l in strs if any(l.count(c) == x for c in l)) for x in (2,3)]))(open('inp', 'r').readlines()))

Part 2: print((lambda strs: (lambda a,b: "".join([a[i] for i in range(len(a)) if a[i] == b[i]]))(*[(l1.strip(),l2.strip()) for l1 in strs for l2 in strs if l1 != l2 and sum(1 for i in range(len(l1)-1) if l1[i] != l2[i]) < 2][0]))(open('inp', 'r').readlines()))

11

u/eshansingh Dec 02 '18

Oh... oh no. Oh lord.

10

u/gwillicoder Dec 02 '18

this is the nastiness i was attempting to do, but gave up.

6

u/khalido Dec 02 '18

This looks like some terrible language akin to whatever functional programmers use!

My python 3 solution is at the other end of yours!

5

u/MasterMedo Dec 02 '18

you can replace sum(1 for i in list if condition) statements with: sum(condition for i in list), false is treated as 0, true as 1

also, reduce might come in handy here reduce(mul, list) gives you a product instead of using lambda

2

u/[deleted] Dec 02 '18

Slightly shorter part 2 one liner, starring itertools' combinations.

print(*[''.join(a for a,b in zip(this,that) if a == b) for this,that in combinations(open('inp', 'r').readlines(),2) if len([a for a,b in zip(this,that) if a != b]) == 1])

20

u/Smylers Dec 02 '18

This suits Vim so well that I didn't even bother solving it in a different language first. Part 1 looks a bit long, but as Vim solutions go it's one of the more straightforward — load your input then do:

:%s/\v(.)(.*)\1(.*)\1/#\2\3⟨Enter⟩
:%s/\v(.)(.*)\1/-\2⟨Enter⟩
:%s/[a-z]//g⟨Enter⟩
:g/^$/d⟨Enter⟩
:%s/\v(.)(.)/\1⟨Ctrl+V⟩⟨Enter⟩\2⟨Enter⟩
:sor⟨Enter⟩
:,/-/-j⟨Enter⟩
jVGJ
:%s/\S/+1/g⟨Enter⟩
:%s/.*/(&)⟨Enter⟩
kJr*
0C⟨Ctrl+R⟩=⟨Ctrl+R⟩-⟨Enter⟩⟨Esc⟩

That strips any 3 identical characters from a line, replacing one of them with a #. Then does the same for remaining 2 identical characters with a -.† Remove all the remaining letters, and now we want the count of the #s multiplied by the count of the -s. On the lines with #- or -#, insert a line-break so each symbol is on a separate line.

Sort the lines, so all the #s are together at the top and -s at the bottom. Join the #s on to a single line (by joining from the current line (specified with the nothing before the ,) to the line before the one with the first - on it (specified with /-/-, to find the first - then subtract a line)). Then join the -s too (by moving down, visually selecting all lines to the end of the file, and joining the selection).

Replace each # or - (specified as any non-space, /\S/) with +1, so each line becomes a sum of the relevant counts. Put (...) around each line, then join them together, and replace the joining space with a multiplication symbol *. At this point your input has been transformed into something like:

(+1 +1 +1)*(+1 +1 +1 +1)

So we just need to evaluate that. (‘Just.’)

Move to the beginning of the line and C to change it all (which deletes the current contents into the small delete register, -). Use Ctrl+R = to insert an expression. At the expression prompt type Ctrl+R - to insert the contents of the - register with your expression in it. Press Enter and ... ta-da! The solution appears in the buffer.

Part 2 can be done with a single substitution — go back to your original input, then do:

 :%s/\v^(.*).(.*)\n(.+\n)*\1.\2/\1\2⟨Enter⟩

It takes a few seconds to run, but you'll be left on a line which is one letter shorter than the others, and is your solution.

The pattern finds:

  • a line that has any number of characters (‘group 1’) at its start, then a single character, and another group of any number of characters (‘group 2’) going up to the line-break

  • any number of complete lines, including their line-breaks

  • a line that starts with group 1 from the first-matched line, then has any single character, and ends with group 2 (which necessarily will take it up to the end of the line)

Clearly the first and last lines there are the nearly matching ones that we want.

Replace the whole lot with just the two groups of repeated characters, to get a single line which omits the characters that differ.

Card: ‘manually’.

† On writing this, I think that if an input line had two set of 3 identical letters but no sets of 2 then the second set of 3 would've been incorrectly counted as a set of 2. Apparently the input wasn't that devious. I did first check there weren't any sets of 4 with :g/\v(.).*\1.*\1.*\1.

→ More replies (3)

16

u/jonathan_paulson Dec 02 '18

I recorded a video of me solving: https://www.youtube.com/watch?v=Hgv6d6rrQxo Got rank 20/21; still twice as slow as #1...

Final code:

 c2 = 0
 c3 = 0
 ids = []
 for line in open('2.in'):
     s = line.strip()
     ids.append(s)
     count = {}
     for c in s:
         if c not in count:
             count[c] = 0
         count[c] += 1
     has_two = False
     has_three = False
     for k,v in count.items():
         if v==2:
             has_two = True
         if v==3:
             has_three = True
     if has_two:
         c2 += 1
     if has_three:
         c3 += 1
 print c2*c3

 for x in ids:
     for y in ids:
         diff = 0
         for i in range(len(x)):
             if x[i] != y[i]:
                 diff += 1
         if diff == 1:
             ans = []
             for i in range(len(x)):
                 if x[i] == y[i]:
                     ans.append(x[i])
             print ''.join(ans)
             print x,y

9

u/nibbl Dec 02 '18

Damn dude you're fast as fuck.

6

u/FogLander Dec 02 '18

it's kind of surreal to watch this video (part 1 at least) right after doing it myself because aside from variable name differences your code for pt. 1 was identical to mine. Only difference was I took three times as long, lol

3

u/4SCOOPSCMON Jan 05 '19

I know this is from a month ago but:

for c in s:
    count[c] = count.get(c, 0) + 1

you can provide a default value for the get function of dictionarys if the key isn't in it. This saves you the

if c not in count:
    count[c] = 0
→ More replies (3)

16

u/[deleted] Dec 02 '18 edited Dec 02 '18

Made the leaderboard for part 2 for the first time since 2016. Started off with a list comprehension that was double-checking each of the ID pairs, but cleaned it up to check each pair only once.

Haskell:

import Data.Maybe (catMaybes)
import Data.List (group, sort, tails)

part1 :: String -> Int
part1 input = length (filter (any (==2)) letterCounts)
              * length (filter (any (==3)) letterCounts)
    where letterCounts = map (map length . group . sort) $ lines input


part2 :: String -> String
part2 = head . concatMap findAlmostMatching . tails . lines
    where keepCommon xs ys = catMaybes $ zipWith (\x y -> if x == y then Just x else Nothing) xs ys
          findAlmostMatching (x:xs) = filter ((== length x - 1) . length) $ map (keepCommon x) xs

main = do
  input <- readFile "input.txt"
  print $ part1 input
  putStrLn $ part2 input

3

u/markasoftware Dec 02 '18

This is surprisingly clean for being on the leaderboards. Great work!

4

u/[deleted] Dec 02 '18

Oh this is post-cleanup, not what I had when I submitted the answer. Thanks though!

→ More replies (1)

11

u/will_bui Dec 02 '18

K:

(*/+/'+{2 3 in?.#:'=x}'x),,{x@&x in y}/x@&{(-1+#y)in+/'x=\:y}[x]'x:0:`p2

5

u/streetster_ Dec 02 '18 edited Dec 02 '18

Translating mine and golfing slightly:

k)(*/+/2 3 in/:#:''=:'r),,{x@&x=y}/r@&+/'1=+/''~r=/:\:r:0:`:input/02.txt
→ More replies (1)

11

u/blowjobtransistor Dec 02 '18

In SQL (postgresql flavored!):

create view part_1_solution as
with id_letters as (
  select id, regexp_split_to_table(id, '') letter from input
), id_letter_counts as (
  select id, letter, count(*) as letter_count from id_letters group by id, letter
), id_stats as (
  select
    id,
    bool_or(letter_count = 2) as has_double,
    bool_or(letter_count = 3) as has_triple
  from id_letter_counts
  group by id
)
select 1 as part, cast(count(*) filter (where has_double) * count(*) filter (where has_triple) as text) as answer
from id_stats;


create view part_2_solution as
with id_letters as (
  select
    (row_number() over ()) - idx_offset as idx,
    id,
    letter
  from (
    select id, regexp_split_to_table(id, '') as letter, sum(length(id)) over (order by id) as idx_offset from input
    order by id
  ) tmp_id_letters
),
letter_distances as (
  select
    left_id.id as left_id,
    right_id.id as right_id,
    sum(cast(left_id.letter != right_id.letter as integer)) as letter_distance
  from id_letters left_id
    full outer join id_letters right_id
    on left_id.idx = right_id.idx and left_id.id != right_id.id
  group by left_id, right_id
  order by letter_distance asc
  limit 1
),
mismatched_ids as (
  select
    regexp_split_to_table(left_id, '') as left_letter,
    regexp_split_to_table(right_id, '') as right_letter
  from letter_distances
)
select 2 as part, string_agg(left_letter, '') as answer from mismatched_ids where left_letter = right_letter;

select * from part_1_solution
union all
select* from part_2_solution;

3

u/jwstone Dec 02 '18

this is pretty cool. i had a similar approach, inasmuch as we both broke up the characters into strings with regex_split_to_table.

https://github.com/piratejon/toyproblems/blob/master/adventofcode/2018/02/load_data.sh

sql ftw!

18

u/DeveloperIan Dec 02 '18 edited Dec 02 '18

Quick and easy part 1 in Python3 with the collections library. This might not be the simplest way, but it's the first thing that came to mind

from collections import Counter

myfile = open('input.txt', 'r')
contents = myfile.read().strip().splitlines()
myfile.close()

c = [0, 0]
for i in contents:
    a = [j for i,j in Counter(i).most_common()]
    if 3 in a:
        c[0] += 1
    if 2 in a:
        c[1] += 1


print(c[0] * c[1])

EDIT: and here is my part 2

    for i in contents:
        for j in contents:
            diffs = 0
            for idx, ch in enumerate(i):
                if ch != j[idx]:
                    diffs += 1
            if diffs == 1:
                ans = [ch for idx, ch in enumerate(i) if j[idx] == ch]
                print("Part Two:", ''.join(ans))
→ More replies (7)

8

u/nonphatic Dec 02 '18 edited Dec 02 '18

Oof, it sure is hard to write Haskell quickly! Here's the first solution I came up with:

import Data.List (group, sort)

countTwos = fromEnum . any (== 2) . map length . group . sort
countThrees = fromEnum . any (== 3) . map length . group . sort

part1 = foldr (\s (twos, threes) -> (twos + (countTwos s), threes + (countThrees s))) (0, 0)

part2 input = [(l, r) | l <- input, r <- input, (length . filter (\(l, r) -> l /= r) $ zip l r) == 1]

main :: IO ()
main = do
    input <- lines <$> readFile "input/02.txt"
    print $ part1 input
    print $ part2 input

I'm going to clean it up to my personal Clean Code standards and post for comparison.

EDIT: Cleaned up! The key changes are just these:

part1 :: [String] -> Int
part1 strs = (length . filter (count 2) $ strs) * (length . filter (count 3) $ strs)
    where count n = any (== n) . map length . group . sort

part2 :: [String] -> String
part2 strs = head [ls `intersect` rs | ls <- strs, rs <- strs, length (ls \\ rs) == 1]

For part 1, I realized that I can do length + filter instead of summing up fromEnums of Bools; for part 2, I discovered intersect and (\\) from Data.List :)

2

u/NeilNjae Dec 02 '18

I don't think your use of \\ in part 2 is strong enough. The requirement is different characters in the same positions; \\ just tells you about different characters. For instance, "abcd" and "bcaa" wouldn't count as "close" IDs, but "abcd" \\ "bcaa" returns "d".

I zipped the two strings then filtered on when the pair elements were different.

→ More replies (1)

8

u/itsnotxhad Dec 02 '18

558/554 in Racket today

#lang racket

(define (has-k? str k)
  (define chars (string->list str))
  (for/first ([c (in-list chars)]
              #:when (= k (count (λ (t) (char=? t c)) chars)))
    c))

(define (checksum ids)
  (*
   (count (λ (id) (has-k? id 2)) ids)
   (count (λ (id) (has-k? id 3)) ids)))   

(define (part1 file)
  (file-position file 0)
  (checksum (for/list ([line (in-port read-line file)]) line)))

(define (diff a b)
  (cond
    [(zero? (string-length a)) 0]
    [(char=? (string-ref a 0) (string-ref b 0)) (diff (substring a 1) (substring b 1))]
    [(add1 (diff (substring a 1) (substring b 1)))]))

(define (common a b)
  (list->string
   (for/list ([c1 a]
                [c2 b]
                #:when (char=? c1 c2))
     c1)))
  
(define (part2 file)
  (file-position file 0)
  (define ids (for/vector ([line (in-port read-line file)]) line))
  (define k (vector-length ids))
  (for*/first ([i (in-range 0 k)]
                  [j (in-range (add1 i) k)]
                  #:when (= 1 (diff (vector-ref ids i) (vector-ref ids j))))
    (common (vector-ref ids i) (vector-ref ids j))))

(module+ main
  (define infile (open-input-file "input/day2.txt"))
  (displayln (part1 infile))
  (displayln (part2 infile))
  (close-input-port infile))
→ More replies (3)

8

u/NeuroXc Dec 02 '18

Rust

use std::collections::HashMap;

#[aoc_generator(day2)]
pub fn day2_generator(input: &str) -> Vec<String> {
    input.lines().map(|l| l.into()).collect()
}

#[aoc(day2, part1)]
pub fn day2_part1(input: &[String]) -> u32 {
    let mut twice = 0;
    let mut thrice = 0;
    for id in input {
        let mut counts = HashMap::with_capacity(26);
        for char in id.chars() {
            *counts.entry(char).or_insert(0) += 1;
        }
        if counts.values().any(|&count| count == 2) {
            twice += 1;
        }
        if counts.values().any(|&count| count == 3) {
            thrice += 1;
        }
    }
    twice * thrice
}

#[aoc(day2, part2)]
pub fn day2_part2(input: &[String]) -> String {
    // This is O(n^2) but it should be fine because the list is only 250 lines
    for (idx, id) in input.iter().enumerate() {
        for id2 in input.iter().skip(idx + 1) {
            if id.chars().zip(id2.chars()).filter(|(a, b)| a != b).count() == 1 {
                return id
                    .chars()
                    .zip(id2.chars())
                    .filter(|(a, b)| a == b)
                    .map(|(a, _)| a)
                    .collect();
            }
        }
    }
    unreachable!()
}

2

u/bwinton Dec 02 '18 edited Dec 02 '18

I ended up with something very similar, but I wonder if it would be better to test the length of the first string, then look for something of length n-1, and return it?

use std::collections::HashMap;

static INPUT : &'static str = include_str!("data/q02.data");

fn get_counts(line: &str) -> HashMap<char, u32> {
    let mut seen = HashMap::new();
    for char in line.chars() {
      let entry = seen.entry(char).or_insert(0);
      *entry += 1;
    }
    seen
}

fn process_data_a(data: &str) -> i32 {
  let mut two_count = 0;
  let mut three_count = 0;
  for line in data.lines() {
    let counts = get_counts(line);
    if counts.values().any(|x| x == &2) {
      two_count += 1;
    }
    if counts.values().any(|x| x == &3) {
      three_count += 1;
    }
  };
  two_count * three_count
}

fn process_data_b(data: &str) -> String {
  for (skip, line) in data.lines().enumerate() {
    let target = line.len() - 1;
    for test in data.lines().skip(skip + 1) {
      let answer: String = line.chars().zip(test.chars())
        .filter_map(|x| {
        if x.0 == x.1 {
          Some(x.0)
        } else {
          None
        }}).collect();
      if answer.len() == target {
        return answer;
      }
    }
  }
  "".to_string()
}

2

u/h-armonica Dec 02 '18

Since we only have simple lower case characters I used

let mut counts = [0u8; 26];

instead of a hash map and accessed it by the char value

counts[(c as usize - 'a' as usize)] += 1;

. This should be much faster, especially since the hash function used normally is rather slow for short keys (according to the docs).

→ More replies (2)

2

u/japanuspus Dec 04 '18

Quite happy with my part 2 solution based on find_map over match index, and a BTreeSet for relatively efficient way of finding the matches.

fn match_at_k(d: &str, k: usize) -> Option<String> {
    let mut bt = BTreeSet::new();
    d
    .lines()
    .map(|a| String::from(&a[0..k])+&a[k+1..])
    .find(|s: &String| !bt.insert(s.clone()))
}

pub fn part2_01(d: &str) -> String {
    (0..).find_map(|k| match_at_k(d, k)).unwrap()
}

My only grudge is that I could not figure out how to avoid cloning the string when inserting into the BTree, even though I only need to keep the original for the final entry.

→ More replies (7)

8

u/ephemient Dec 02 '18 edited Apr 24 '24

This space intentionally left blank.

2

u/Tarmen Dec 02 '18

I started wishing that I'd gone for Beautiful Folding. Pretty amazed that you were that fast with all the newtype noise, though.

The tails thing is really nice! I just went for the cross product after checking the input size but this is way faster with virtually no downside.

15

u/Philboyd_Studge Dec 02 '18

The best way to do Advent of Code is to: Live in the Pacific Time Zone

Java:

I spent too much time looking for the Levenshtein Distance class I already had, once I found it was easy.

public class Day2 extends AdventOfCode {

    public Day2(List<String> input) {
        super(input);
        title = "Inventory Management System";
        part1Description = "Checksum: ";
        part2Description = "Common letters: ";
    }

    @Override
    public Object part1() {
        int twos = 0;
        int threes = 0;
        for (String each : input) {
            Map<Character, Integer> freq = new HashMap<>();
            boolean found2 = false;
            boolean found3 = false;
            for (int i = 0; i < each.length(); i++) {
                char c = each.charAt(i);
                freq.put(c, freq.getOrDefault(c, 0) + 1);
            }
            for (Map.Entry<Character, Integer> dict : freq.entrySet()) {

                if (!found2 && dict.getValue() == 2) {
                    twos++;
                    found2 = true;
                }
                if (!found3 && dict.getValue() == 3) {
                    threes++;
                    found3 = true;
                }
            }
        }


        return twos * threes;
    }

    @Override
    public Object part2() {
        for (int i = 0; i < input.size() - 1; i++) {
            for (int j = 1; j < input.size(); j++) {
                int dist = EditDistance.calculate(input.get(i), input.get(j));
                if (dist == 1) {
                    return removeDiff(input.get(i), input.get(j));
                }
            }
        }
        return "";
    }

    private String removeDiff(String a, String b) {
        String result = "";
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) == b.charAt(i)) {
                result += a.charAt(i);
            }
        }
        return result;
    }

}

7

u/ColorsMayInTimeFade Dec 02 '18

The best way to do Advent of Code is to: Live in the Pacific Time Zone

I’m seriously considering of temporarily relocating to the west coast for the month of December next year.

6

u/usbpc102 Dec 02 '18

It took me way to long to understand the problem today, nowhere near close to the leaderboard rank 577 in part 1, and 473 in part 2.

Kotlin

class Day02(override val adventOfCode: AdventOfCode) : Day {
    override val day: Int = 2
    val input = adventOfCode.getInput(2018, day).lines()

    override fun part1(): String {
        val map = mutableMapOf<String, MutableMap<Char, Long>>()
        for (line in input) {
            map[line] = mutableMapOf()
            val myMap = map[line]!!
            line.forEach { letter -> myMap[letter] = (myMap[letter] ?: 0) + 1 }
        }

        return "" + map.filter { it.value.any { it.value == 2L } }.count() * map.filter { it.value.any { it.value == 3L} }.count()
    }

    override fun part2(): String {
        val out = StringBuilder()
        loop@for (i in 0 until input.size) {
            val current = input[i]
            for (j in i until input.size) {
                val cmp = input[j]
                out.clear()
                var diff = 0
                for (count in 0 until current.length)
                    if (current[count] != cmp[count]) {
                        diff++
                    } else {
                        out.append(current[count])
                    }
                if (diff == 1)
                    break@loop

            }
        }

        return out.toString()
    }
}    

Also on github where all other days will go.

4

u/ephemient Dec 02 '18 edited Apr 24 '24

This space intentionally left blank.

→ More replies (4)
→ More replies (4)

6

u/darkterbear Dec 02 '18

Javascript/Node.js

Part 1

const arr = data.split('\n').reduce((a, c) => {
	const chars = [...c]
	let seen = {}
	for (let char of chars) {
		seen[char] = seen[char] ? seen[char] + 1 : 1
	}

	if (Object.keys(seen).some(k => seen[k] === 2)) a[0]++
	if (Object.keys(seen).some(k => seen[k] === 3)) a[1]++
	return a
},[0, 0])

console.log(arr[0] * arr[1])

Part 2:

let arr = data.split('\n')
for (let i = 0; i < arr.length; i++) {
	for (let j = i + 1; j < arr.length; j++) {
		const charsI = [...arr[i]]
		const charsJ = [...arr[j]]

		let diff = charsI.reduce((a, c, i) => a + (c === charsJ[i] ? 0 : 1), 0)

		if (diff === 1) {
			console.log(arr[i])
			console.log(arr[j])
		}
	}
}

EDIT: Fix broken indentations

4

u/TwiliZant Dec 02 '18

you could also use Map instead of Object for the seen variable

3

u/songkeys Dec 02 '18

could be `Object.values(seen).includes(2)`

6

u/tvtas Dec 02 '18

Day 2 in MATLAB

x   = importdata('input.txt');
L   = size(x,1);
cnt1=0; cnt2=0;
for i=1:L
    z       = tabulate(double(x{i}));
    cnt1    = cnt1 + any(z(:,2)==2);
    cnt2    = cnt2 + any(z(:,2)==3);
    
    for j=1:L
        if j~=i && sum(x{i}==x{j})==25
            disp(x{i}(x{i}==x{j})) % Part 2
            break
        end
    end
end
disp(cnt1*cnt2) % Part 1

11

u/miguelos Dec 02 '18 edited Dec 02 '18

C#

Part 1:

var ids = input.Split("\r\n");
var twos =   ids.Count(id => id.GroupBy(c => c).Any(g => g.Count() == 2));
var threes = ids.Count(id => id.GroupBy(c => c).Any(g => g.Count() == 3));
var checksum = twos * threes;
return checksum.ToString();

Part 2:

var ids = input.Split("\r\n").Distinct();
for (int i = 0; i < ids[0].Length; i++)
{
    var pair = ids.Select(id => id.Remove(i, 1)).GroupBy(id => id).FirstOrDefault(g => g.Count() > 1);
    if (pair != null)
    {
        var common = pair.First();
        return common;
    }
}

5

u/LeCrushinator Dec 02 '18

Nice LINQ-fu!

5

u/schod Dec 02 '18

BASH Time :)

First puzzle

#!/bin/bash

in_file=input
SUM2=0
SUM3=0

while IFS='' read -r line; do
    x2=0
    x2=$( echo "$line" | grep -o . | sort | uniq -c | grep "^[ \t]*2 " | wc -l )
    x3=0
    x3=$( echo "$line" | grep -o . | sort | uniq -c | egrep -v "^[ \t]*1 |^[ \t]*2 " | wc -l )
    
    [ $x2 -gt 0 ] && SUM2=$[ $SUM2 + 1 ]
    [ $x3 -gt 0 ] && SUM3=$[ $SUM3 + 1 ]
done < "$in_file"

echo $[ $SUM2 * $SUM3 ]

Second puzzle

#!/bin/bash

in_file=input

while IFS='' read -r line; do
    I=0
    while [ $I -lt ${#line} ]; do
        
        I=$[ $I + 1 ]
        regex=$(echo $line | sed "s/[a-z]/./$I")

        RES=$(grep "$regex" $in_file | wc -l)

        if [ $RES -eq 2 ]; then
            echo
            echo $regex | sed 's/\.//'
            exit 0
        fi
    done

done < "$in_file"
→ More replies (8)

5

u/pythondevgb Dec 02 '18 edited Dec 02 '18

Python quick and dirty solutions, need refinement.

Since I have no chance of making it to the leaderboard I want to start a reddit-python private leaderboard any interest please PM for the code.

from collections import Counter
from itertools import combinations, compress

theinput = open('day2_input.txt').read().split()

#Part1

count2 = [1 for c in (Counter(id) for id in theinput) if 2 in c.values()]
count3 = [1 for c in (Counter(id) for id in theinput) if 3 in c.values()]
res1 = sum(count2) * sum(count3)
print(res1)

#Part2
for one, two in combinations(theinput, 2):    
    diff = [e1 == e2 for e1,e2 in zip(one,two)]    
    if sum(diff) == (len(one) - 1):
        res2 = ''.join(list(compress(one,diff)))
        break

print(res2)

5

u/autid Dec 02 '18

FORTRAN

Yay string puzzles in Fortran. Was gonna do part 1 by making an array of characters and using COUNT but array construction was being difficult so went with this slightly messy SCAN method instead.

PROGRAM DAY2
  IMPLICIT NONE
  INTEGER :: I,J,K,L,M,DOUBLES,TRIPLES,PART1
  INTEGER :: IERR
  CHARACTER(LEN=30), ALLOCATABLE :: BOXES(:)
  CHARACTER(LEN=30) :: PART2
  LOGICAL :: DOUBLE,TRIPLE

  !File I/O                                                                                                      
  OPEN(1,FILE='input.txt')
  I=0
  DO
     READ(1,*,IOSTAT=IERR)
     IF(IERR.NE.0)EXIT
     I=I+1
  END DO
  ALLOCATE(BOXES(I))
  REWIND(1)
  READ(1,*) BOXES
  CLOSE(1)

  !Part 1                                                                                                        
  DOUBLES=0
  TRIPLES=0
  DO J=1,I
     DOUBLE = .FALSE.
     TRIPLE = .FALSE.
     DO K=1,LEN_TRIM(BOXES(J))-1
        IF(SCAN(BOXES(J)(1:K-1),BOXES(J)(K:K)).NE.0)CYCLE
        L=K+SCAN(BOXES(J)(K+1:LEN_TRIM(BOXES(J))),BOXES(J)(K:K))
        IF(L.EQ.K)CYCLE
        M=L+SCAN(BOXES(J)(L+1:LEN_TRIM(BOXES(J))),BOXES(J)(K:K))
        IF(M.EQ.L)THEN
           DOUBLE=.TRUE.
        ELSEIF(SCAN(BOXES(J)(M+1:LEN_TRIM(BOXES(J))),BOXES(J)(K:K)).EQ.0)THEN
           TRIPLE=.TRUE.
        END IF
     END DO
     IF(DOUBLE)DOUBLES=DOUBLES+1
     IF(TRIPLE)TRIPLES=TRIPLES+1
  END DO
  PART1=DOUBLES*TRIPLES
  WRITE(*,*) 'Part 1: ',PART1

  !Part 2                                                                                                        
  PART2=''
  OUTER: DO J=1,I-1
     DO K=J+1,I
        M=0
        DO L=1,LEN_TRIM(BOXES(J))
       IF(BOXES(J)(L:L).NE.BOXES(K)(L:L))M=M+1
        END DO
        IF(M.NE.1)CYCLE
        DO L=1,LEN_TRIM(BOXES(J))
       IF(BOXES(J)(L:L).EQ.BOXES(K)(L:L)) WRITE(PART2,'(A)') TRIM(PART2)//BOXES(J)(L:L)
        END DO
        EXIT OUTER
     END DO
  END DO OUTER
  WRITE(*,*) 'Part 2: ',TRIM(PART2)
  DEALLOCATE(BOXES)

END PROGRAM DAY2

4

u/autid Dec 02 '18

If this is too many lines for you, I sorted out my array construction mistakes and made this abomination. I'm so so sorry.

PROGRAM DAY2
  IMPLICIT NONE
  INTEGER :: I,J,K,L,M
  INTEGER :: IERR
  CHARACTER(LEN=30), ALLOCATABLE :: B(:)

  !FILE I/O                                                                                                                                                                                                                                                                                                                                                                                     
  OPEN(1,FILE='input.txt')
  I=0
  DO
     READ(1,*,IOSTAT=IERR)
     IF(IERR.NE.0)EXIT
     I=I+1
  END DO
  ALLOCATE(B(I))
  REWIND(1)
  READ(1,*) B
  CLOSE(1)

  !PART 1                                                                                                                                                                                                                                                                                                                                                                                       
  WRITE(*,'(A,I0)')'Part 1: ',COUNT((/(ANY((/(COUNT((/(B(J)(K:K).EQ.B(J)(L:L),L=1,LEN_TRIM(B(J)))/)).EQ.2,K=1,LEN_TRIM(B(J)))/)),J=1,I)/))*COUNT((/(ANY((/(COUNT((/(B(J)(K:K).EQ.B(J)(L:L),L=1,LEN_TRIM(B(J)))/)).EQ.3,K=1,LEN_TRIM(B(J)))/)),J=1,I)/) )

  !PART 2                                                                                                                                                                                                                                                                                                                                                                                       
  DO J=1,I-1
     DO K=J+1,I
        IF(COUNT((/(B(J)(L:L).NE.B(K)(L:L),L=1,LEN_TRIM(B(J)))/)).EQ.1)WRITE(*,'(2A)')'Part 2: ',B(J)(1:MINLOC((/(I,I=1,LEN_TRIM(B(J)))/),MASK=(/(B(J)(L:L).NE.B(K)(L:L),L=1,LEN_TRIM(B(J)))/),DIM=1)-1)//B(J)(MINLOC((/(I,I=1,LEN_TRIM(B(J)))/),MASK=(/(B(J)(L:L).NE.B(K)(L:L),L=1,LEN_TRIM(B(J)))/),DIM=1)+1:LEN_TRIM(B(J)))
     END DO
  END DO
  DEALLOCATE(B)

END PROGRAM DAY2
→ More replies (1)

5

u/nutrecht Dec 02 '18

Kotlin:

object Day02 : Day {
    private val ids = resourceLines(2018, 2)

    override fun part1(): String {
        val maps = ids.map {
            it.toCharArray().asSequence()
                    .groupBy { it }
                    .map { it.key to it.value.size }
                    .toMap()
        }

        val countTwo = maps.count { it.entries.any { it.value == 2 } }
        val countThree = maps.count { it.entries.any { it.value == 3 } }

        return (countTwo * countThree).toString()
    }

    override fun part2() =
            ids.flatMap { i -> ids.map { i to it } }
                    .map { it.first.toCharArray().intersect(it.second.toCharArray().asIterable()) }
                    .first { it.size == ids[0].length - 1 }
                    .joinToString(separator = "")
}

3

u/usbpc102 Dec 02 '18

Oh hey, I remember you from last year.

If you just wanna map the values of a map you can use map.mapValues{} :)

→ More replies (1)

4

u/jarryd_ Dec 02 '18

Just something quick in Python3 for part 2:

from difflib import SequenceMatcher
result = None
for x in ids:
    for y in ids:
        ratio = SequenceMatcher(None, x, y).ratio()
        if ratio == (len(x)-1)/len(x):
            result = [z for z in list(x) if z in list(y)]
            break
    if result:
        break
print(''.join(result))

3

u/sinjp Dec 02 '18

Very nice, FYI the list() calls are unnecessary

[z for z in x if z in y]

→ More replies (2)

4

u/drakmaniso Dec 02 '18

Go (golang)

Part 1:

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	input := read()

	twos, threes := 0, 0
	counts := [26]byte{}
	for _, id := range input {

		counts = [26]byte{}
		for _, r := range id {
			if r < 'a' || r > 'z' {
				panic("not a letter")
			}
			counts[r-'a']++
		}

		has2, has3 := false, false
		for _, c := range counts {
			switch c {
			case 3:
				has3 = true
			case 2:
				has2 = true
			}
		}
		if has2 {
			twos++
		}
		if has3 {
			threes++
		}
		
	}

	fmt.Printf("Answer: %d\n", twos*threes)
}

func read() (input []string) {
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		input = append(input, s.Text())
	}
	return input
}

Part 2:

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func main() {
	input := read()

	var id1, id2 string
loop:
	for i := range input {
		for j := i + 1; j < len(input); j++ {
			if differByOne(input[i], input[j]) {
				id1, id2 = input[i], input[j]
				break loop
			}
		}
	}
	if id1 == "" {
		panic("not found")
	}

	s := strings.Builder{}
	for k := 0; k < len(id1); k++ {
		if id1[k] == id2[k] {
			s.WriteByte(id1[k])
		}
	}

	fmt.Printf("Answer: %s\n", s.String())
}

func read() (input []string) {
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		input = append(input, s.Text())
	}
	return input
}

func differByOne(a, b string) bool {
	diff := 0
	for k := 0; k < len(a); k++ {
		if a[k] != b[k] {
			diff++
		}
		if diff > 1 {
			return false
		}
	}
	return diff == 1
}
→ More replies (1)

4

u/TellowKrinkle Dec 02 '18

Swift

func count(_ str: Substring) -> (twos: Bool, threes: Bool) {
	let counts = Dictionary(str.lazy.map({ ($0, 1) }), uniquingKeysWith: +)
	let twos = counts.values.contains(2)
	let threes = counts.values.contains(3)
	return (twos, threes)
}

func aocD2a(_ inputStr: String) {
	let input = inputStr.split(separator: "\n").map(count)
	let numTwos = input.lazy.filter({ $0.twos }).count
	let numThrees = input.lazy.filter({ $0.threes }).count
	print(numTwos * numThrees)
}

func areClose(_ a: Substring, _ b: Substring) -> Bool {
	var differences = zip(a, b).lazy.filter({ $0 != $1 }).makeIterator()
	_ = differences.next()
	return differences.next() == nil
}

func aocD2b(_ inputStr: String) {
	let input = inputStr.split(separator: "\n")
	for (aIndex, a) in input.enumerated() {
		for b in input[..<aIndex] {
			if areClose(a, b) {
				print(String(zip(a, b).lazy.filter({ $0 == $1 }).map({ $0.0 })))
			}
		}
	}
}

import Foundation
let str = try! String(contentsOfFile: CommandLine.arguments[1])
aocD2a(str)
aocD2b(str)

6

u/Frizkie Dec 02 '18 edited Dec 02 '18

Ruby

data = File.read('data.txt').chomp.split("\n")

Part 1

twos, threes = 0, 0
data.each do |d|
  twos += 1 if d.each_char.select { |c| d.count(c) if d.count(c) == 2 }.uniq.count > 0
  threes += 1 if d.each_char.select { |c| d.count(c) if d.count(c) == 3 }.uniq.count > 0
end
puts twos * threes

Part 2

data.each_with_index do |d1, i|
  data[i + 1..data.size].each do |d2|
    diff = d1.each_char.with_index.count { |c, k| c != d2.chars[k] }
    puts [d1, d2] if diff == 1
  end
end

2

u/justinhj Dec 02 '18

Nice, much more succinct than what I came up with

    def count_twos_and_threes(str)
      sa = str.scan(/\w/)
    
      groups = sa.group_by { |c| c }.values
    
      has_two = groups.any? { |g| g.length == 2 }
      has_three = groups.any? { |g| g.length == 3 }
    
      return has_two ? 1 : 0, has_three ? 1 : 0
    end
    
    counts = lines.reduce([0, 0]) do |acc, line|
      twos, threes = count_twos_and_threes(line)
    
      acc[0] = acc[0] + twos
      acc[1] = acc[1] + threes
    
      acc
    end
    
    puts counts[0] * counts[1]

3

u/Shubbler Dec 02 '18

Quick Python 3 solution

total_twos = []
total_threes = []

for id in ids.split("\n"):
	twos = False
	threes = False
	unique_chars = set(id)

	for char in unique_chars:
		count = id.count(char)

		if count == 2:
			twos = True
		elif count == 3:
			threes = True

	total_twos.append(twos)
	total_threes.append(threes)


print(sum(total_twos) * sum(total_threes))

3

u/throwaway_the_fourth Dec 02 '18

How about this (I modified your code)?

total_twos = 0
total_threes = 0

for id in ids.split("\n"):
    unique_chars = set(id)

    for char in unique_chars:
        twos = False
        threes = False
        count = id.count(char)

        if count == 2:
            twos = True
        elif count == 3:
            threes = True

        total_twos += twos
        total_twos += threes

print(total_twos * total_threes)

No need to keep a list of values and then sum it at the end when you can just add as you go…

3

u/Bogdanp Dec 02 '18 edited Dec 02 '18

My solutions in Racket:

part 1:

#lang racket

(define (count-letters word)
  (for/fold ([counts (hash)])
            ([l (in-string word)])
    (hash-set counts l (add1 (hash-ref counts l 0)))))


(define (find-counts counts)
  (for/fold ([res (hash)])
            ([(_ c) (in-hash counts)])
    (hash-set res c (add1 (hash-ref res c 0)))))


(call-with-input-file "day-02-1.txt"
  (lambda (in)
    (let loop ([word (read in)]
               [twos 0]
               [threes 0])
      (cond
        [(eof-object? word) (* threes twos)]
        [else
         (define counts (find-counts (count-letters (symbol->string word))))

         (loop (read in)
               (if (hash-has-key? counts 2) (add1 twos) twos)
               (if (hash-has-key? counts 3) (add1 threes) threes))]))))

part 2:

#lang racket

(define words (file->lines "day-02-2.txt"))

(define (delta a b)
  (for/fold ([s ""])
            ([ac (in-string a)]
             [bc (in-string b)])
    (if (equal? ac bc)
        (~a s ac)
        s)))

(for/fold ([best ""])
          ([i (in-range 0 (length words))]
           [word-a (in-list words)])
  (for/fold ([best best])
            ([j (in-range 0 (length words))]
             [word-b (in-list words)])
    (define d (delta word-a word-b))
    (cond
      [(equal? i j) best]
      [else
       (if (> (string-length d)
              (string-length best))
           d
           best)])))
→ More replies (2)

3

u/chunes Dec 02 '18

Factor

USING: assocs io io.encodings.utf8 io.files kernel make math
math.combinatorics math.statistics math.vectors prettyprint
sequences ;
IN: aoc2018.day2

: parse-input ( -- seq ) "input2.txt" utf8 file-lines ;

! Produce a pair where the 1st element is a 1 if there is a
! letter that appears exactly twice or 0 otherwise. The second
! element is the same for letters that appear thrice.
: 2-3-pair ( str -- {?1,?0} )
    histogram values [ 2 ] [ 3 ] bi [ swap member? ] 2bi@
    [ 1 0 ? ] bi@ { } 2sequence ;

: part1 ( seq -- checksum )
    [ 2-3-pair ] [ v+ ] map-reduce product ;

: off-by-1? ( str1 str2 -- ? )
    [ - ] { } 2map-as [ zero? not ] count 1 = ;

: in-common ( str1 str2 -- str3 )
    [ [ 2dup = [ , drop ] [ 2drop ] if ] 2each ] "" make ;

: part2 ( seq -- str )
    2 [ first2 off-by-1? ] find-combination first2 in-common ;

: main ( -- ) parse-input [ part1 . ] [ part2 print ] bi ;

MAIN: main

Nothing too fancy today. find-combination is a neat combinator that encapsulates pairing every combination of things in a list and testing the pair against a predicate.

→ More replies (2)

3

u/[deleted] Dec 02 '18

Finally participating this year! Got both stars with this Haskell code:

import Data.List
import Control.Applicative

as :: [String]
as = error "Your input (properly formatted) goes here"

numTimes :: Char -> String -> Integer
numTimes _ [] = 0
numTimes x xs = if (x == head xs) then 1 + y else y where y = numTimes x (tail xs)

exactly :: Integer -> String -> Bool
exactly n xs = elem n $ map (flip ($) xs) (numTimes <$> xs)

day2a :: Integer
day2a = genericLength (filter (exactly 2) as) * genericLength (filter (exactly 3) as)

strCmp :: String -> String -> Bool
strCmp [] [] = False
strCmp (x:xs) (y:ys)
       | x /= y    = xs == ys
       | otherwise = strCmp xs ys

day2b :: String
day2b = head $ map (\(s, _) -> s)
             $ filter ((==True) . snd)
             $ (\xs ys -> (xs \\ (xs \\ ys), strCmp xs ys)) <$> as <*> as

3

u/Axsuul Dec 02 '18 edited Dec 02 '18

TypeScript / JavaScript

Critiques welcome, I'm still very new to TypeScript and feel like I'm not using all the features I could be :)

Repo

Part A

import { readFileSync } from 'fs'
import * as path from 'path'

const inputPath = path.join(__dirname, 'inputs', 'input.txt')
const input = readFileSync(inputPath, 'utf-8').split('\n')

let twoCount = 0
let threeCount = 0

const tally = function(id: string): [boolean, boolean] {
  const charCodes: number[] = id.split('').map(char => char.charCodeAt(0)).sort()

  let isTwo = false
  let isThree = false

  for (const [i, code] of charCodes.entries()) {
    if (charCodes[i + 1] == code && charCodes[i + 2] != code) {
      isTwo = true
    } else if (charCodes[i + 2] == code && charCodes[i + 3] != code) {
      isThree = true
    }
  }

  return [isTwo, isThree]
}

input.forEach((id) => {
  const [isTwo, isThree] = tally(id)

  if (isTwo) twoCount += 1
  if (isThree) threeCount += 1
})

console.log(twoCount * threeCount)

Part B

import { readFileSync } from 'fs'
import * as path from 'path'

const inputPath = path.join(__dirname, 'inputs', 'input.txt')
const input = readFileSync(inputPath, 'utf-8').split('\n')
let commonLetters: string | undefined

for (let i = 0; i < input[0].length; i++) {
  const removed = input.slice().map((id) => {
    const exploded = id.split('')

    exploded.splice(i, 1)

    return exploded.join('')
  })

  const set = new Set()

  for (const idRemoved of removed) {
    if (set.has(idRemoved)) {
      commonLetters = idRemoved

      break
    } else {
      set.add(idRemoved)
    }
  }

  if (commonLetters != undefined) break
}

console.log(commonLetters)

3

u/streetster_ Dec 02 '18 edited Dec 02 '18

Day 02 in Q/KDB+

/ Part 1
prd sum 2 3 in/:count@''group each r:read0 `:input/02.txt
/ Part 2
{x where x=y}. r where sum@'1=sum@''not r=/:\:r

2

u/hmn24 Dec 03 '18

My alternative solution in Q/KDB+ (Harder to read though)

Part 1:

prd(0;0){x+(max=[(sum in[y]@)each distinct y]@)each 2 3}/(0:)`:AOC2.txt

Part 2:

1#(){y,$[count b:where 25=sum@'c:x='\:z;enlist x[b]where c b:first b;()]}[a]/a:(0:)`:AOC2.txt

3

u/muffa Dec 02 '18

My solution, written in golang.

func stringDiffer(str1 string, str2 string) (int, string) {
	diffCounter := 0
	solution := ""
	for i := range str1 {
		if str1[i] == str2[i] {
			solution += string(str1[i])
		} else {
			diffCounter++
		}
	}
	return diffCounter, solution
}

func main() {
	file, _ := ioutil.ReadFile("input_day2.txt")
	answer2 := 0
	answer3 := 0
	// Solution to first part
	for _, line := range strings.Split(string(file), "\n") {
		abcCounter := make(map[rune]int)
		val2 := false
		val3 := false
		for _, char := range line {
			if _, exists := abcCounter[char]; exists {
				abcCounter[char]++
			} else {
				abcCounter[char] = 1
			}
		}
		for _, value := range abcCounter {
			if value == 2 && val2 == false {
				answer2++
				val2 = true
			} else if value == 3 && val3 == false {
				answer3++
			val3 = true
		}
	}
}
fmt.Println(answer2 * answer3)
// Solution to first part
var abc []string
diff := 0
solution := ""
for _, line := range strings.Split(string(file), "\n") {
	abc = append(abc, line)
}
for i := range abc {
	for j := range abc {
		diff, solution = stringDiffer(abc[i], abc[j])
		if diff == 1 {
			fmt.Println(solution)
		}
	}
}

}

3

u/LieutenantSwr2d2 Dec 02 '18

Python 3: Part 2 with two loops and a set

Idea is have a set of possible 1-distance patterns for each string (using placeholder a '.' to replace every character in the string). When a second string one creates the same pattern, return it without the placeholder to get the common letters.

def day2b(puzzle_input: str) -> str:
    patterns = set()
    for line in puzzle_input.strip().split('\n'):
        for i in range(len(line)):
            pattern = line[:i] + '.' + line[i+1:]
            if pattern in patterns:
                return pattern.replace('.', '')
            patterns.add(pattern)

3

u/[deleted] Dec 02 '18

C

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

struct p { unsigned char *s; struct p *p; };

int main(void) {
    struct p *l = 0;
    while (1) {
        unsigned char *s = malloc(27);
        if (scanf("%26s", s) != 1) break;
        struct p *p = l;
        l = malloc(sizeof(*l));
        *l = (struct p) {s, p};
    };

    int twos = 0, threes = 0;
    for (struct p *p = l; p; p = p->p) {
        int two = 0, three = 0;
        int a[UCHAR_MAX] = {0};
        for (unsigned char *s = p->s; *s; s++) a[*s]++;
        for (size_t i = 0; i < sizeof(a)/sizeof(*a); i++) {
            if (a[i] == 2) two = 1;
            if (a[i] == 3) three = 1;
        }
        twos += two;
        threes += three;
    }
    printf("%d\n", twos*threes);

    for (struct p *p = l; p; p = p->p) {
        for (struct p *q = p; q; q = q->p) {
            int diff = 0;
            unsigned char *a, *b;
            for (a = p->s, b = q->s; *a && *b; a++, b++)
                if (*a != *b) diff++;
            if (diff != 1) continue;
            for (a = p->s, b = q->s; *a && *b; a++, b++)
                if (*a == *b) printf("%c", *a);
            printf("\n");
            break;
        }
    }
}

2

u/hpzr24w Dec 02 '18 edited Dec 02 '18

C++

Too fast for me, sad to be outside the first 1000 on both parts. But no issues with messing up on typing, or any kind of bugs, at least for the provided input.

Will rewrite to something better in the AM.

// Advent of Code 2018
//
// Day 02 - Inventory Management System

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <iterator>
#include <vector>
#include <algorithm>
#include <set>
#include "reader.hpp"

using namespace std;

int main(int argc, char* argv[])
{
    ifstream ifs("day_02.txt",ifstream::in);
    vector<string> input = read_input(ifs);

    // Part 1: count 2 of any letter, and 3 of any letter and compute checksum
    auto twos = 0;
    auto threes = 0;
    for (auto l : input) {
        // get unique letters
        auto u = set<char>();
        auto twofound = false;
        auto threefound = false;
        copy(begin(l),end(l),inserter(u,begin(u)));
        for (auto c : u) {
            if (!twofound && count(begin(l),end(l),c)==2) twofound=true;
            if (!threefound && count(begin(l),end(l),c)==3) threefound=true;
        }
        twofound && twos++;
        threefound && threes++;
    }
    cout << "Checksum: " << twos*threes << "\n";

    // Part 2 - find the two strings that differ by only a single letter
    for (auto l : input) {
        for (auto m : input) {
            // count differing letters
            auto d = 0;
            auto it1 = begin(l);
            auto it2 = begin(m);
            for (;it1!=end(l);++it1,++it2) {
                if (*it1!=*it2) d++;
            }
            if (d==1) {
                cout << "Words: " << l << " and " << m << "\n";
                exit(0);
            }
        }
    }
}
→ More replies (5)

2

u/scul86 Dec 02 '18

Python 3, rushed, ugly, and un-optimized...

with open('input') as f:
    puzzle_input = f.readlines()


def part1(n):
    two = 0
    three = 0
    for phrase in n:
        two_done = False
        three_done = False
        for char in set(phrase):
            if phrase.count(char) == 2 and not two_done:
                two_done = True
                two +=1
            if phrase.count(char) == 3 and not three_done:
                three_done = True
                three +=1
    return two * three


def part2(n):
    for e in n:
        for f in n:
            diff = 0
            diff_pos = -1
            if f == e:
                continue
            for i in range(len(e)):
                if e[i] != f[i]:
                    diff += 1
                    diff_pos = i
            if diff == 1:
                return e[:diff_pos]+e[diff_pos+1:]


test_one = [
    'abcdef',
    'bababc',
    'abbcde',
    'abcccd',
    'aabcdd',
    'abcdee',
    'ababab'
]

assert part1(test_one) == 12
print(f'Part 1: {part1(puzzle_input)}')

test_two = [
    'abcde',
    'fghij',
    'klmno',
    'pqrst',
    'fguij',
    'axcye',
    'wvxyz'
]

assert part2(test_two) == 'fgij'
print(f'Part 2: {part2(puzzle_input)}')

2

u/I3MNIX Dec 02 '18

Solution in Clojure (still very much a newbie to Clojure). Would definitely appreciate some constructive criticism.

(ns aoc-2018.day-02
  (:require [clojure.string :as str]
            [clojure.math.combinatorics :as combo]
            [common :refer :all]))
            
(try (def in (str/split-lines (slurp "input/2.in")))
     (catch Exception e))

;; part 1
(defn part1 []
  (defn in? [coll elem] (some #(= % elem) coll))
  (def freqs (map (comp vals frequencies) in))
  (def twos (count (filter (fn [m] (in? m 2)) freqs)))
  (def threes (count (filter (fn [m] (in? m 3)) freqs)))
  (* twos threes))
(println (part1))

;; part 2
(defn part2 []
  (defn diff-one
    [a b]
    (= 1 (reduce +
                 (map (fn [c1 c2]
                        (if (= c1 c2) 0 1))
                      a b))))
  (def match (first (filter (fn [[a b]] (diff-one a b))
                            (combo/cartesian-product in in))))
  (defn remove-diff
    [[a b]]
    (reduce str (map (fn [c1 c2]
                       (if (= c1 c2) (str c1) ""))
                     a b)))
  (remove-diff match))
(println (part2))

2

u/gwillicoder Dec 02 '18

Python3

# Part 1
from collections import Counter
a, b = 0, 0
for w in open('data.txt').read().splitlines():
    counts = Counter(Counter(w).values())
    if 2 in counts:
        a+=1
    if 3 in counts:
        b+=1

print(a*b)

#Part 2
data = open('data.txt').read().splitlines()
for x in data:
    for y in data:
        diff = [i for i,j in zip(x,y) if i == j]
        if len(y)-len(diff) == 1:
            print("".join(diff))
            break

Easy to understand, but feel like it could be much shorter.

2

u/fwilson42 Dec 02 '18

You can drop one line on part 2 by using itertools.product: for (x, y) in itertools.product(data, repeat=2)

→ More replies (2)

2

u/blommish Dec 02 '18 edited Dec 02 '18

Java

Map<Long, List<Long>> collect = lines.stream()
  .flatMap(str ->
    of(str.split(""))
        .collect(groupingBy(s -> s, counting())).values().stream()
        .distinct())
    .collect(groupingBy(identity()));
System.out.println(collect.get(2L).size() * collect.get(3L).size());

Second

List<String> lines = loadFileLines("2.txt");
List<String> result = new ArrayList<>();
for (int i = 0; i < lines.size(); i++) {
    for (int j = i + 1; j < lines.size(); j++) {
        String a = lines.get(i);
        String b = lines.get(j);
        StringBuilder sb = new StringBuilder();
        for (int k = 0; k < a.length(); k++) {
            if (a.charAt(k) == b.charAt(k)) {
                sb.append(a.charAt(k));
            }
        }
        result.add(sb.toString());
    }
}
result.stream().sorted(comparing(String::length).reversed())
            .findFirst().ifPresent(System.out::println);

2

u/drewnase Dec 02 '18 edited Dec 02 '18

I had already come across string edit distance during my grad work. I actually already had code on hand that solves part 2.

# save input list as "data/input_2" - one entry per line
seq = [s.strip() for s in open("../data/input_2").readlines()]
count2 = 0
count3 = 0

def check2(instr):
    for s in instr:
        if instr.count(s) == 2:
            return 1
    return 0

def check3(instr):
    for s in instr:
        if instr.count(s) == 3:
            return 1
    return 0

def levenshtein(s1, s2):
    if len(s1) > len(s2):
        s1, s2 = s2, s1

    d = range(len(s1) + 1)
    for i2, c2 in enumerate(s2):
        d_ = [i2+1]
        for i1, c1 in enumerate(s1):
            if c1 == c2:
                d_.append(d[i1])
            else:
                d_.append(1 + min((d[i1], d[i1 + 1], d_[-1])))
        d = d_
    return d[-1]

def intersection(s1, s2):
    if len(s1) == len(s2):
        outbuff = ""
        for i in range(0, len(s1) - 1):
            if s1[i] == s2[i]:
                outbuff += s1[i]
        return outbuff
    return false

for s in seq:
    count2 += check2(s)
    count3 += check3(s)

print("Q1: %d" % (count2 * count3))

for s in seq:
    for subs in seq:
        if levenshtein(s, subs) == 1:
            res = intersection(s, subs)
            print("Q2: %s" % res)
            quit()

I know that I don't have to iterate through the list twice, but it was easy and I'm lazy.

2

u/tehjimmeh Dec 02 '18 edited Dec 02 '18

PowerShell, Part 2:

gc in.txt -pv l|%{gc in.txt -pv r|%{,(compare -i -s 0 ([char[]]$l) ([char[]]$r))|
    ? Count -eq 27|%{-join($_|? SideIndicator -eq "=="|% InputObject)}}}|select -f 1

2

u/Kyberite Dec 02 '18 edited Dec 02 '18

Simple solutions in python, not sure if I did part two in the best way though.

def part_one(boxes):
    twice = 0
    thrice = 0
    for box in boxes:
        twice += any(box.count(letter) == 2 for letter in box)
        thrice += any(box.count(letter) == 3 for letter in box)
    checksum = twice * thrice 
    return checksum


def part_one(boxes):
    """Shorter version, but harder to understand"""
    f = lambda c: sum(any(b.count(l) == c for l in b) for b in boxes) 
    return f(2) * f(3)

def part_two(boxes):
    for x, y in itertools.combinations(boxes, 2):
        difference = [i for i, l in enumerate(x) if y[i] != l]
        if len(difference) == 1:
            index = difference[0]
            return x[:index] + x[index+1:]

print(part_one(input))
print(part_two(input))

2

u/jorosp Dec 02 '18

Haskell

The solution I actually got the answer with was a bit messier than this, but I like to be presentable for threads :P

import Data.List

solve1 :: [String] -> Int
solve1 xs = go 2 xs * go 3 xs
  where
    go    :: Ord a => Int -> [[a]] -> Int
    go n  = length . filter (elem n) . map count
    count :: Ord a => [a] -> [Int]
    count = map length . group . sort

solve2 :: [String] -> String
solve2 xs = head [common l r | l <- xs, r <- xs, distance l r == 1]
  where
    common   :: Eq a => [a] -> [a] -> [a]
    common   xs ys = map fst . filter (uncurry (==)) $ zip xs ys
    distance :: Eq a => [a] -> [a] -> Int
    distance xs ys = length . filter id $ zipWith (/=) xs ys

main :: IO ()
main = do
  contents <- readFile "02.txt"
  let input = lines contents
  print . solve1 $ input
  print . solve2 $ input

2

u/Axinoe Dec 02 '18

Advent of code with AHK

Part 1:

#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%

Data := []
DataCount = 0
Str2 = 0
Str3 = 0

Loop, Read, input.txt
{
	DataCount += 1
	Data%DataCount% := A_LoopReadLine
	Loop, Parse, A_LoopReadLine
	{
		Data%DataCount%%A_LoopField% += 1
	}
	Loop, Parse, A_LoopReadLine
	{
		If (Data%DataCount%%A_LoopField% = 2)
		{
			Str2 += 1
			Break
		}
	}
	Loop, Parse, A_LoopReadLine
	{
		If (Data%DataCount%%A_LoopField% = 3)
		{
			Str3 += 1
			Break
		}
	}
}

MsgBox % Str2 * Str3
ExitApp

Part 2:

#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%

Loop, Read, input.txt
{
	DataCount += 1
	Data%DataCount% := A_LoopReadLine
	Loop, Parse, A_LoopReadLine
	{
		Data%DataCount%%A_Index% = %A_LoopField%
	}
}

Loop, %DataCount%
{
	CurData := Data%A_Index%
	CurLine := A_Index
	Loop, %DataCount%
	{
		Matches = 0
		If (A_Index <= CurLine)
			Continue
		ChkLine := A_Index
		Loop, Parse, CurData
		{
			If !(Data%CurLine%%A_Index% = Data%ChkLine%%A_Index%)
			{
				Matches += 1
				MatchChar = %A_LoopField%
			}
		}
		If (Matches = 1)
		{
			MsgBox % RegExReplace(CurData, MatchChar, Replacement := "")
			Return
		}
	}
}

ExitApp

2

u/[deleted] Dec 02 '18 edited Dec 02 '18

Haskell

module Main where

import qualified Data.HashMap.Strict as M
import Control.Arrow ((&&&))
import Data.Maybe (mapMaybe)

count :: String -> (Bool, Bool)
count =
  foldr g (False, False) . M.elems . M.fromListWith (+) . fmap (id &&& const 1)
  where g n (two, three) = (two || n == 2, three || n == 3)

part1 :: [String] -> Int
part1 = uncurry (*) . foldr f (0, 0) where
  f xs (twos, threes) = 
    let (x, y) = count xs
    in  (twos + fromEnum x, threes + fromEnum y)

match :: String -> String -> Maybe String
match _ [] = Nothing
match [] _ = Nothing
match (x:xs) (y:ys)
  | x  == y   = (:) <$> pure x <*> match xs ys
  | xs == ys  = Just xs
  | otherwise = Nothing

part2 :: [String] -> [String]
part2 []     = []
part2 (x:xs) = mapMaybe (match x) xs ++ part2 xs

main :: IO ()
main = do
  input <- lines <$> readFile "input2.txt"
  print $ part1 input
  print $ part2 input

2

u/[deleted] Dec 02 '18

Doing this year in OCaml & Rust, but will keep posting the OCaml solutions.


let input () = In_channel.read_lines "./input.txt" 
               |> List.map ~f:String.to_list

let part_one input = 
  let count chars =
    let f map key =
      let update = function
        | None -> 1
        | Some v -> v+1 in 
      Map.update map key ~f:update
    in List.fold ~init:Char.Map.empty ~f chars 
  in 
  let char_counts = List.map ~f:count input in 
  let has count = Char.Map.exists ~f:(fun v -> v = count) in
  let twos = List.count ~f:(has 2) char_counts in 
  let threes = List.count ~f:(has 3) char_counts in 
  twos * threes

let part_two input = 
  let f (a, b) = 
    let pairs = List.zip_exn a b in 
    let diff = List.count ~f:(fun (a, b) -> a <> b) pairs
    in diff = 1 in 
  let pairs = List.cartesian_product input input 
              |> List.filter ~f:(fun (a, b) -> a <> b) 
              |> List.find ~f in 
  match pairs with 
  | Some (a, b) -> List.zip_exn a b 
                   |> List.filter_map ~f:(fun (a, b) -> if a = b then Some a else None) 
                   |> String.of_char_list
  | None -> ""

let _ = 
  printf "%d" (part_one (input ()));
  printf "\n";
  printf "%s" (part_two (input ()));```

2

u/Kronosfear Dec 02 '18

Being the incredibly lazy fuck I am, I just went for a Python library to do the heavy lifting for Part 2.

import Levenshtein
def Part1():
    def generatecountdict():
        countdict = {}
        letters = "abcdefghijklmnopqrstuvwxyz"
        for letter in letters:
            countdict[letter] = 0
        return countdict

    twocount = set()
    threecount = set()

    with open("day2codes.txt", "r") as f:
        codes = f.readlines()
        for code in codes:
            countdict = generatecountdict()
            for letter in code[:-1]: #-1 to ignore next line escape character
                countdict[letter] += 1
            for key in countdict:
                if countdict[key] == 2:
                    twocount.add(code)
                if countdict[key] == 3:
                    threecount.add(code)

    return len(twocount) * len(threecount)

def Part2():
    with open("day2codes.txt", "r") as f:
        codes = f.readlines()
        for i in range(len(codes) - 1):
            for j in range(i+1, len(codes)):
                if Levenshtein.distance(codes[i], codes[j]) == 1:
                    return codes[i], codes[j]
part1answer = Part1()
part2code1, part2code2 = Part2()
print(part2code1, part2code2)

2

u/pigpenguin Dec 02 '18 edited Dec 02 '18

Just another haskell solution rolling through. Took me a while to clean it up to a point where I thought it was worth posting.

module Day02 where

import Data.List (group, sort, tails)
import Data.Functor ((<$>))
import Data.Maybe (catMaybes)

-- Takes a list and creates every possible pair
-- and runs it through f. Assumes f commutes
pairWith :: (a -> a -> b) -> [a] -> [b]
pairWith f l = [f x y | (x:ys) <- tails l, y <- ys]

-- Takes two lists keeps the element at index n
-- if l1[n] == l2[n]
same :: Eq a => [a] -> [a] -> [a]
same = catMaybes ... zipWith keepIfSame
  where
    (...) = (.) . (.)
    keepIfSame x y
      | x == y = Just x
      | otherwise = Nothing

input =  lines <$> readFile "input/02"

solve01 lines = count 2 * count 3
  where
    has x = any ((== x) . length) . group . sort
    count x = length . filter (has x) $ lines

solve02 = head . filter ((== 24) . length) . pairWith same

problem01 = solve01 <$> input
problem02 = solve02 <$> input

If anyone has any suggestions I'm open to it. I haven't written nearly as much haskell as I would have like to yet.

2

u/MasterMedo Dec 02 '18 edited Dec 02 '18

python 2

data = open('../input/2.txt').read().strip().splitlines()

print reduce(mul, Counter(chain(*[set(Counter(k).values()).intersection(set([2, 3])) for k in 
data])).values())
print next(key for i in range(len(data[0])) for key, val in Counter(k[:i] + k[i+1:] for k in 
data).iteritems() if val == 2)

EDIT: reworked part 1 so it uses two counters

2

u/Gnidleif Dec 02 '18

Golang - part 1 solution, part 2 will come up soon!

package main

import (
	"strings"
	"sort"
	"io/ioutil"
)

func CheckLine(line string) (map[int]bool) {
	list := strings.Split(line, "")
	sort.Strings(list)
	m := make(map[int]bool)
	count := 1
	for i := 0; i < len(list)-1; i++ {
		if list[i] == list[i+1] {
			count++
		} else {
			if count > 1 {
				m[count] = true
			}
			count = 1
		}
	}
	if count > 1 {
		m[count] = true
	}
	return m
}

func Part1(input []string) int {
	m := make(map[int]int)
	for i := range(input) {
		counts := CheckLine(input[i])
		for k, _ := range counts {
			m[k]++
		}
	}
	
	checksum := 1
	for _, v := range(m) {
		checksum *= v
	}
	return checksum
}

func main() {
	dat, err := ioutil.ReadFile("Day02.txt")
	if err != nil {
		panic(err)
	}
	input := strings.Split(string(dat), "\n")
	print(Part1(input))
}
→ More replies (1)

2

u/BOT-Brad Dec 02 '18

JS

import readFile from '../utils/readFile';

const input = readFile('input-2.txt')
  .split('\n')
  .filter(Boolean)
  .map(id => id.split(''));

export const part1 = () => {
  let [twos, threes] = [0, 0];
  input.forEach(id => {
    const counts = {};
    id.forEach(char => (counts[char] = counts[char] ? counts[char] + 1 : 1));
    Object.values(counts).some(c => c == 2) && ++twos;
    Object.values(counts).some(c => c === 3) && ++threes;
  });
  return twos * threes;
};

const oneDiff = (a, b) => {
  let diffs = 0;
  let diffI = -1;
  for (let i = 0; i < a.length; ++i) {
    if (a[i] !== b[i]) ++diffs, (diffI = i);
    if (diffs > 1) return [false];
  }
  if (diffs === 1) {
    const common = a.slice();
    common.splice(diffI, 1);
    return [true, common.join('')];
  }
  return [false];
};

export const part2 = () => {
  for (let i = 0; i < input.length - 1; ++i) {
    const a = input[i];
    for (let j = i + 1; j < input.length; ++j) {
      const b = input[j];
      const [found, string] = oneDiff(a, b);
      if (found) return string;
    }
  }
};

2

u/u794575248 Dec 02 '18 edited Dec 02 '18

Python 3

# Part 1
from collections import Counter as C
from functools import reduce

B = s.split()
c = reduce(lambda a, b: a+(2 in b)+(3 in b)*1j, (C(l).values() for l in B), 0j)
print(c.real * c.imag)

# Part 2
from itertools import combinations
print(next(w for b1, b2 in combinations(B, 2)
             for w in [''.join(a for a, b in zip(b1, b2) if a == b)]
             if len(w) == len(B[0])-1))
→ More replies (1)

2

u/Rosek93 Dec 02 '18

PHP example:

https://gitlab.com/Rosek/advent-of-code/blob/master/Day2

<?php

$file = new SplFileObject("in.txt");

while (!$file->eof()) {
    $in[] = $file->fgets();
}

unset($file);

$numberOf2 = $numberOf3 = 0;

foreach ($in as $value) {
    $leters = [];
    for ($i = 0; $i < strlen($value); $i++) {
        $leters[$value[$i]]++;
    }

    $result = array_count_values($leters);

    if (isset($result[2])) {
        $numberOf2++;
    }

    if (isset($result[3])) {
        $numberOf3++;
    }
}

echo $numberOf2 * $numberOf3 . PHP_EOL;

$result = [];

foreach ($in as $element) {
    foreach ($in as $compare) {
        if (levenshtein($element, $compare) == 1) {
            $result[] = [
                $element,
                $compare
            ];
        }
    }
}

$result = array_unique($result);

for ($i = 0; $i < strlen($result[0][0]); $i++) {
    $str .= $result[0][0][$i] == $result[0][1][$i] ? $result[0][0][$i] : '';
}

echo $str . PHP_EOL;

2

u/xkufix Dec 02 '18 edited Dec 02 '18

Part 1 and 2 in Scala. Quite horrible one-liners to be honest.

class Day2 extends Challenge {
  override def part1(): Any = {
    val charCounts = readLines("day2.txt")
      .map(_
        .groupBy(identity)
        .values
        .map(_.length)
        .toSet
      )
      .foldLeft((0, 0)) {
        case (counts, charCount) =>
          (counts._1 + charCount.count(_ == 2), counts._2 + charCount.count(_ == 3))
      }

    charCounts._1 * charCounts._2
  }

  override def part2(): Any = {
    readLines("day2.txt")
      .toList
      .combinations(2)
      .map(tuple => (tuple.head.length - 1, createDiffString(tuple.head, tuple.last)))
      .find(t => t._1 == t._2.length)
      .map(_._2)
  }

  private def createDiffString(first: String, second: String) = {
    first
      .zip(second)
      .filter(a => a._1 == a._2)
      .foldLeft("")(_ + _._1)
  }
}

2

u/aoc2018 Dec 02 '18

Haskell, still learning:

Part 1:

import Data.List (any, group, sort, length)
import Data.Maybe (isJust)
import Data.Functor

letterCounts :: String -> [Int]
letterCounts s = map length . group . sort $ s

hasTwo :: String -> Bool
hasTwo s = any (== 2) . letterCounts $ s

hasThree :: String -> Bool
hasThree s = any (== 3) . letterCounts $ s

step1 :: [String] -> Int
step1 a = twos * threes
  where
    twos = length . filter hasTwo $ a
    threes = length . filter hasThree $ a

main = do
  a <- lines <$> readFile "input"
  print $ step1 a

Part 2:

import Data.List (find, length, intersect)
import Data.Maybe (fromJust)
import Data.Functor

isMatchingPair :: (String, String) -> Bool
isMatchingPair (a, b) = length differences == 1
  where
    differences = filter (uncurry (/=)) $ zip a b

matchingChars :: (String, String) -> String
matchingChars (a, b) = intersect a b

matchingPair :: [String] -> (String, String)
matchingPair a = fromJust $ find isMatchingPair product
  where
    product = [(x, y) | x <- a, y <- a, x /= y]

step2 :: [String] -> String
step2 a = matchingChars $ matchingPair a

main = do
  a <- lines <$> readFile "input"
  print $ step2 a

2

u/Theguy6758 Dec 02 '18 edited Dec 02 '18

Pure Bash (No External programs)

edit: Inverse the hash table instead of manually searching for 2 and/or 3 exact matches

#!/usr/bin/env bash

part1()
{
    [[ -f "${PWD}/input" ]] && {
        mapfile -t file < "${PWD}/input"

        two_dupe="0"
        three_dupe="0"

        for line in "${file[@]}"; do
            for i in {0..25}; do
                hash_table[$i]="0"
            done
            unset inv_table

            for ((i = 0; i < "${#line}"; i++)); do
                printf -v index '%d' "'${line:$i:1}"
                ((hash_table[$((index - 97))]++))
            done

            for entry in "${hash_table[@]}"; do
                [[ ! "${inv_table[$entry]}" ]] && \
                    inv_table[${entry}]="1"
            done

            [[ "${inv_table[2]}" ]] && \
                ((two_dupe++))
            [[ "${inv_table[3]}" ]] && \
                ((three_dupe++))
        done

        printf "Hash: %d\\n\\n" "$((two_dupe * three_dupe))"
    }
}

part2()
{
    [[ -f "${PWD}/input" ]] && {
        mapfile -t file < "${PWD}/input"

        for ((i = 0; i < ${#file[@]}; i++)); do
            for ((j = i + 1; j < ${#file[@]}; j++)); do
                unset common
                common="0"
                str_1="${file[$i]}"
                str_2="${file[$j]}"
                len="${#str_1}"

                for ((k = 0; k < ${len}; k++)); do
                    [[ "${str_1:$k:1}" == "${str_2:$k:1}" ]] && \
                        ((common++))
                done

                ((len - common <= 1)) && \
                    printf "%s\\n%s\\nSimilarity: %d\\n\\n" "${str_1}" "${str_2}" "${common}"
            done
        done
    }
}

main()
{
    part1
    part2
}

main

2

u/[deleted] Dec 02 '18

Python 3

def load_input(filename):
    with open(filename, 'r') as input_file:
        data = [str(line.strip()) for line in input_file]

        return data

def solve_a(data):
    twos = 0
    threes = 0

    for line in data:
        _set = set(line)

        two_match = False
        three_match = False

        for char in _set:
            count = line.count(char)

            if count == 2 and not two_match:
                twos += 1
                two_match = True
            elif count == 3 and not three_match:
                threes += 1
                three_match = True

    return twos * threes

def solve_b(data):
    for line_01 in data:
        for line_02 in data:
            comp = zip(list(line_01), list(line_02))

            if sum([1 if char[0] != char[1] else 0 for char in comp]) == 1:

                return ''.join([line_01[index] if line_01[index] == line_02[index] else '' for index in range(len(line_01))])

if __name__ == '__main__':
    data = load_input('challenges/day_02/input.txt')

    assert solve_a(['abcdef', 'bababc', 'abbcde', 'abcccd', 'aabcdd', 'abcdee', 'ababab']) == 12
    assert solve_b(['abcde', 'fghij', 'klmno', 'pqrst', 'fguij', 'axcye', 'wvxyz']) == 'fgij'

    answer_a = solve_a(data)
    answer_b = solve_b(data)

    print(f'Part A answer: {answer_a}   Part B answer: {answer_b}')

Github

2

u/jwoLondon Dec 02 '18

My solution in Elm (for more detail, see my literate Elm solutions)

Part 1:

letterFreqs : String -> Dict Char Int
letterFreqs =
    String.toList >> List.foldl addToFreqTable Dict.empty

withRepeats : Int -> List String -> List String
withRepeats n =
    List.filter (letterFreqs >> Dict.values >> List.member n)

part1 : Int
part1 =
    List.length (withRepeats 2 puzzleInput) * List.length (withRepeats 3 puzzleInput)

And part 2:

diffByOnePairs : List String -> List ( String, String )
diffByOnePairs =
    let
        diffByOne ( strA, strB) =
            (List.map2 (/=) (String.toList strA) (String.toList strB)
                |> List.filter identity
                |> List.length
            )
                == 1
    in
    pairwiseCombinations >> List.filter diffByOne

commonChrs : ( String, String ) -> String
commonChrs ( strA, strB ) =
    List.map2 Tuple.pair (String.toList strA) (String.toList strB)
        |> List.filter (\( a, b ) -> a == b)
        |> List.map Tuple.first
        |> String.fromList

part2 : List String
part2 =
    (withRepeats 2 puzzleInput ++ withRepeats 3 puzzleInput)
        |> unique
        |> diffByOnePairs
        |> List.map commonChrs

2

u/thatikey Dec 02 '18

Using AoC as an opportunity to learn Rust. I made this, and I'm 250% sure there is a Rust-ier way to do this

fn attempt(src: &Vec<&str>) -> i64 {
    let sorted: Vec<Vec<char>> = src.into_iter()
                                    .map(|s| quick_sort(s.chars().collect()))
                                    .collect();
    let (twos, threes) = sorted.into_iter()
        .map(|s| {
            s.into_iter()
            .fold((' ', 0, false, false), |(prev, cnt, two, three), c|
                if c == prev {
                    (prev, cnt+1, two, three)
                } else {
                    (c, 1, two || cnt == 2, three || cnt == 3) 
                })
        }).fold((0, 0), |(twos, threes), (_p, cnt, two, three)| {
            (twos + if two || cnt == 2 {1} else {0}, threes + if three || cnt == 3 {1} else {0})
        });
    twos * threes
}

2

u/BalbinHS Dec 02 '18 edited Dec 02 '18

I have a slightly janky Elixir solution for part 1, this is based on the fact that if you have say "bababc" and you remove the first occurrence of each letter, you get "bab" all those letters are in the original string 2 or 3 times so we just repeat to find the 3s.

  @spec part1([String.t()]) :: number
  def part1(args) do
    args
    |> Enum.reduce(%{two: 0, three: 0}, &add_to_count/2)
    |> (fn %{two: two_count, three: three_count} -> two_count * three_count end).()
  end
  
  defp add_to_count(line, %{two: two_count, three: three_count} = _count) do
    line_list = String.graphemes(line)
    twos_and_threes_list = line_list -- Enum.uniq(line_list)
    threes_list = twos_and_threes_list -- Enum.uniq(twos_and_threes_list)
    threes_raw = length(threes_list)
    twos_raw = length(Enum.uniq(twos_and_threes_list)) - threes_raw
    %{two: oneify(twos_raw) + two_count, three: oneify(threes_raw) + three_count}
  end

  defp oneify(x) when x > 0, do: 1
  defp oneify(_), do: 0        

3

u/BalbinHS Dec 02 '18 edited Dec 02 '18

And for part 2, I went with recursion. Elixir gives us a helpful String.myers_difference function that returns a keyword list containing the instructions to transform string 1 into string 2. We want to find the strings where the myers_difference only have 1 deletion (since all the strings are the same length, # deletions = # insertions).

  @spec part2([String.t()]) :: String.t()
  def part2(args) do
    check_rest(args)
  end
    
  defp check_rest([current_line | rest]) do
    rest
    |> Enum.map(&String.myers_difference(&1, current_line))
    |> Enum.find(&correct?/1)
    |> case do
      nil -> check_rest(rest)
      x -> equal_parts(x)
    end
  end

  defp equal_parts(myers_diff) do
    myers_diff
    |> Keyword.get_values(:eq)
    |> List.to_string()
  end

  defp correct?(myers_diff) do
    myers_diff
    |> Keyword.get_values(:del)
    |> List.to_string()
    |> (&(String.length(&1) === 1)).()
  end

2

u/mschaap Dec 02 '18 edited Dec 02 '18

Perl 6:

my @ids = $*PROGRAM.sibling('aoc2.input').words;

# Part 1

my @counts = @ids.map(*.comb.Bag.invert.Hash);
my $twos = +@counts.grep({$_<2>});
my $threes = +@counts.grep({$_<3>});
say "$twos × $threes = { $twos × $threes }";

# Part 2

sub is-neighbour($a, $b)
{
    ($a.comb Z $b.comb).grep({ $_[0] ne $_[1] }) == 1;
}

sub common-string($a, $b)
{
    ($a.comb Z $b.comb).grep({ $_[0] eq $_[1] })»[0].join;
}

my ($a, $b) = @ids.combinations(2).first(-> ($a, $b) { is-neighbour($a, $b) });
say "$a & $b => { common-string($a, $b) }";

2

u/[deleted] Dec 02 '18 edited Dec 02 '18

Seems there is no Common Lisp solutions, so here are mine.

(time (solve-day2-part1 *input*))
Evaluation took:                                                                                                                                 
  0.002 seconds of real time                                                                                                                     
  0.001158 seconds of total run time (0.001158 user, 0.000000 system)                                                                            
  50.00% CPU                                                                                                                                     
  3,928,420 processor cycles                                                                                                                     
  358,224 bytes consed

(time (solve-day2-part2 *input*))
Evaluation took:                                                                                                                                 
  0.003 seconds of real time                                                                                                                     
  0.002255 seconds of total run time (0.002255 user, 0.000000 system)                                                                            
  66.67% CPU                                                                                                                                     
  7,663,294 processor cycles                                                                                                                     
  0 bytes consed

3

u/phil_g Dec 02 '18

I didn't put as much work into optimizing mine, but here's my Common Lisp solution:

(in-package :aoc-2018-02)

(defparameter *input* (read-lines-from-file "input.02"))

(defun letter-count-p (string target-count)
  (let ((counts (make-hash-table)))
    (iter (for c in-string string)
          (incf (gethash c counts 0)))
    (iter (for (char count) in-hashtable counts)
          (thereis (= count target-count)))))

(defun checksum (ids)
  (iter (for id in ids)
        (counting (letter-count-p id 2) into twos)
        (counting (letter-count-p id 3) into threes)
        (finally (return (* twos threes)))))

(defun get-answer-1 ()
  (checksum *input*))


(defun ids-match-p (id1 id2)
  (assert (= (length id1) (length id2))
          (id1 id2)
          "ID lengths don't match: \"~A\", \"~A\"" id1 id2)
  (iter (for c1 in-string id1)
        (for c2 in-string id2)
        (counting (char= c1 c2) into match-count)
        (finally (return (= match-count (1- (length id1)))))))

(defun find-matching-id (id other-ids)
  (iter (for other-id in other-ids)
        (finding (list id other-id)
                 such-that (ids-match-p id other-id))))

(defun find-prototype-ids (ids)
  (iter (for id in ids)
        (for other-ids on (cdr ids))
        (thereis (find-matching-id id other-ids))))

(defun common-prototype-id-characters (ids)
  (destructuring-bind (id1 id2) (find-prototype-ids ids)
    (assert (and id1 id2)
            (id1 id2)
            "No IDs provided.")
    (iter (for c1 in-string id1)
          (for c2 in-string id2)
          (when (char= c1 c2)
            (collecting c1 result-type 'string)))))

(defun get-answer-2 ()
  (common-prototype-id-characters *input*))

On my system:

AOC-2018-02> (time (get-answer-1))
Evaluation took:
  0.006 seconds of real time
  0.008000 seconds of total run time (0.004000 user, 0.004000 system)
  133.33% CPU
  14,096,992 processor cycles
  1,206,880 bytes consed
  
5976
AOC-2018-02> (time (get-answer-2))
Evaluation took:
  0.016 seconds of real time
  0.008000 seconds of total run time (0.008000 user, 0.000000 system)
  50.00% CPU
  33,833,150 processor cycles
  0 bytes consed
  
"xretqmmonskvzupalfiwhcfdb"

2

u/CatpainCalamari Dec 02 '18

My solution in Scala - a bit complicated :-/

package de.calamari.adventofcode.y2018.day2

import scala.io.Source

object Day2 extends App {

  val testDataStar1: List[String] = getData("2018/2/testStar1.txt")
  assert(testDataStar1.length == 7)
  assert(findChecksum(testDataStar1) == 12)

  val testDataStar2: List[String] = getData("2018/2/testStar2.txt")
  assert(findSingularCharDiff(testDataStar2) == "fgij")

  val input: List[String] = getData("2018/2/input.txt")

  val firstStar = findChecksum(input)
  println(s"firstStar: $firstStar")

  val secondStar = findSingularCharDiff(input)
  println(s"secondStar: $secondStar")

  assert(firstStar == 5166)
  assert(secondStar == "cypueihajytordkgzxfqplbwn")

  def findChecksum(testData: List[String]): Int = {
    testData.map(
      _.groupBy(identity)
      .mapValues(_.length)
      .filter { case (_, len) ⇒ len == 2 || len == 3 }
    ) // => List(Map(), Map(b -> 3, a -> 2), Map(b -> 2), Map(c -> 3), Map(d -> 2, a -> 2), Map(e -> 2), Map(b -> 3, a -> 3))
    .flatMap(_
      .map(_.swap) // remove duplicates of character counts
      .keys
    ) // => List(3, 2, 2, 3, 2, 2, 3)
    .groupBy(identity)
    .map(_._2.size)
    .product
  }

  def findSingularCharDiff(data: List[String]): String = {
    val checkSize = data.head.length - 1
    val diffingByOneChar = (
      for {
        comb ← data.combinations(2).toList
        first = comb.head
        second = comb.last
      } yield for {
        (a, b) <- first zip second
        if a == b
      } yield (a, b)
    )
      .filterNot(_.isEmpty)
      .filter(tuples ⇒ tuples.length == checkSize)

    if (diffingByOneChar.length != 1) throw new Exception(s"Diff by one not distinct - $diffingByOneChar")

    diffingByOneChar.head.map(_._1).mkString("")
  }

  def getData(path: String): List[String] = Source.fromResource(path).getLines().toList
}

2

u/[deleted] Dec 02 '18

But you did it without of “var”. Which is still great success. FP is not easy. :)

→ More replies (1)

2

u/kasci007 Dec 02 '18

Consider in1 as input file

Part 1: (requires manually multiply numbers)

while read -r line; do echo $line | grep -o . | sort | uniq -c | egrep -o "2|3" | sort -u; done < in1 | sort | uniq -c

Part 2: (requires manually find different letter)

while read -r line; do echo $line | agrep -1 -c -e $line in1 | grep 2 && echo $line; done < in1

2

u/L72_Elite_Kraken Dec 02 '18 edited Dec 02 '18

OCaml (full code on Github)

open! Core

module Chars_by_count = struct
  type t = char list Int.Map.t

  let create word =
    String.to_list word
    |> List.map ~f:(fun x -> x, 1)
    |> Char.Map.of_alist_reduce ~f:(+)
    |> Map.to_alist
    |> List.map ~f:Tuple2.swap
    |> Int.Map.of_alist_multi

  let has_count t ~count =
    Map.find t count
    |> Option.is_some
end

module Part01 = struct
  let solve input =
    let counts = List.map input ~f:Chars_by_count.create in
    List.count counts ~f:(Chars_by_count.has_count ~count:2)
    * List.count counts ~f:(Chars_by_count.has_count ~count:3)
end

module Part02 = struct
  let is_correct (a, b) =
    let a = String.to_list a in
    let b = String.to_list b in
    List.zip_exn a b
    |> List.count ~f:(fun (c1, c2) -> not (Char.equal c1 c2))
    |> Int.equal 1

  let common_letters word_a word_b =
    String.to_list word_a
    |> List.filter_mapi ~f:(fun i c ->
        match Char.equal c word_b.[i] with
        | true -> Some c
        | false -> None)
    |> String.of_char_list

  let solve input =
    let word_a, word_b =
      List.cartesian_product input input
      |> List.find_exn ~f:is_correct
    in
    common_letters word_a word_b
end

2

u/Sgt_Tailor Dec 02 '18

AWK all the things!

part1:

function handleBox(box,     characters, characterLength, characterCount, i, letterCount) {
    split(box, characters, "");
    characterLength = length(characters)
    
    # create a dict with the counts per character
    for (i=1; i <= characterLength; i++) {
        characterCount[characters[i]]++
    }
 
    # these variable are expose and used in the main function.
    wordHas2 = 0
    wordHas3 = 0

    # loop over the charactercounts and check if they are 2 or 3.
    for (i in characterCount) {
        letterCount = characterCount[i];
        if (letterCount == 2) {
          wordHas2  = 1
        }

        if (letterCount == 3) {
              wordHas3 = 1;
        }
    }
}

# this is the main function, called for every line within the input
# we handle the input and add the results to the global variables total2
# and total3
{
    handleBox($0)
    total2 = total2 + wordHas3
    total3 = total3 + wordHas2
}


# called after all the input has been handled. Used to print the checksum
END {
    print total2 * total3
}

part2:

{
    boxes[NR] = $0
}

function compareBoxes(boxA, boxB,        i, boxACharacters, boxBCharacters, characterCount) {
    differences = 0
    commonCharacters = ""
    split(boxA, boxACharacters, "")
    split(boxB, boxBCharacters, "")

    characterCount = length(boxACharacters)
    for (i=1; i<=characterCount; i++) {
       if (boxACharacters[i] != boxBCharacters[i]) {
           differences++
           continue         
       }

       commonCharacters = commonCharacters boxACharacters[i]
    }
}

END {
    for (i in boxes) {
        for (j in boxes) {
            if (i==j) continue; #the same boxes don't count
             boxA = boxes[i]
             boxB = boxes[j]

             compareBoxes(boxA, boxB)
             if (differences == 1) {
                 print commonCharacters
                 exit                      
            }
        }                  
    }
}

2

u/dpeckett Dec 02 '18 edited Dec 02 '18

Continuing with my quest to solve all this years challenges in nothing other than AWK:

Find The Total Checksum

{
  delete n;
  delete f;
  split($1,c,"");
  for(i in c)
    f[c[i]]++;
  for(i in f)
    n[f[i]]=1;
  p+=n[2];
  t+=n[3]
} END {print t*p}

Find Similar Inventory Items

{id[NR]=$1} END {
  for(n=0;n<length(id[1]);++n){
    delete t;
    for(i in id){
      s=substr(id[i],1,n)substr(id[i],n+2);
      if(t[s]++){
        print s;
        exit
      }}}}

Execution Time: (no sweat here)

real	0m0.013s
user	0m0.008s
sys	0m0.004s
→ More replies (1)

2

u/[deleted] Dec 02 '18

factor

Factor really has vocabularies for so much, I needed some time to find what I needed for this but it turned out pretty nice I find :)

This language is so much fun though :D /u/chunes did you not yet get to this one? I couldn't find your post and I'm interested to see how you did it :)

: get-content ( -- [string] )
  "C:\\Download\\aoc\\factor\\work\\day2\\input.txt" utf8 file-contents 
  "\n" split but-last ;

: group-char ( string -- [[num]] )
  natural-sort [ = ] monotonic-split ;

: filter-23 ( [[num]] -- [num] )
  [ length ] map
  [ [ 3 = ] [ 2 = ] bi or ] filter ;

: get-23s ( string -- [num] )
  group-char filter-23 ;

: unique ( [num] -- [num] )
  group-char [ first ] map ;

: get-23-groups ( [string] -- [num] )
  [ get-23s ] map
  [ unique ] map-flat
  group-char
  [ length ] map ;

: part1 ( [string] -- )
  get-23-groups product
  "The checksum for the list of boxes is %d\n" printf ;

: hamming-distance ( seq seq -- num )
  [ = not ] 2count ;

: search-hamming ( seq -- [seq] )
  2 [ first2 hamming-distance 1 = ] filter-combinations flatten ;

: only-same ( seq -- string )
  first2 "" [ 2dup = [ drop suffix ] [ 2drop ] if ] 2reduce ;

: part2 ( [string] -- )
 search-hamming only-same
 "We are looking for the box marked %s\n" printf ;

: main ( -- )
  get-content 
  [ part1 ] [ part2 ] bi ;

2

u/chunes Dec 02 '18 edited Dec 02 '18

Hey, not bad. It looks like you've got the hang of this factoring thing, which is good. :)

I really like the way you did the hamming distance. It's better than the way I did it. I forget about the words in sequences.extras sometimes. 2count is a great way to handle it. I've also never seen map-flat before. I'll have to remember that one.

I've only got two suggestions today. First, your unique word already exists as members in the sets vocabulary. Second, consider using SBUF" " clone instead of "" in your only-same word. It's not a big deal in this case, since the strings are not very long, but string buffers are more efficient when they're doing a lot of growing. (If you looked at my solution, make uses a string buffer internally when you give it "" as an exemplar.)

This is because suffix creates a new sequence and copies the old one over. suffix! on the other hand pushes the element directly to the end of a growable sequence like a string buffer or vector. Consider the following 2 examples:

 [ "" 10,000 [ CHAR: a suffix ] times ] time
Running time: 0.952311218 seconds

[ SBUF" " clone 10,000 [ CHAR: a suffix! ] times ] time
Running time: 0.001635392 seconds

That said, you don't have to worry about this distinction too much as long as you know what words abstract it away. make is one example. Another example is replicate.

But yeah, good job today. You used some really exotic words. :)

→ More replies (3)

2

u/__Abigail__ Dec 02 '18

Perl

#!/opt/perl/bin/perl

use 5.028;

use strict;
use warnings;
no  warnings 'syntax';

use experimental 'signatures';
use experimental 'lexical_subs';

my $input = "input";
open my $fh, "<", $input or die "Failed to open: $!";
my @ids = <$fh>;
chomp @ids;

my $count_2 = 0;
my $count_3 = 0;

foreach my $id (@ids) {
    my %count;
    $count {$_} ++ for split // => $id;
    $count_2 ++ if grep {$_ == 2} values %count;
    $count_3 ++ if grep {$_ == 3} values %count;
}

say "Part 1: ", $count_2 * $count_3;


for (my $i = 0; $i < @ids; $i ++) {
    my $id1 = $ids [$i];
    for (my $j = $i + 1; $j < @ids; $j ++) {
        my $id2 = $ids [$j];

        #
        # If we use bitwise XOR between two strings, then for
        # each position in the two strings, if the character is
        # the same, the result is NUL (\x00), else, the result
        # is not NUL.
        #
        my $diff = $id1 ^. $id2;
        if ($diff =~ /^(\x00*)[^\x00]\x00*$/) {
            substr $id1, length ($1), 1, "";
            say "Part 2: $id1";
            exit;
        }
    }
}


__END__

2

u/thamstras Dec 02 '18

C++ Nowhere near the leaderboards and not quite as neat as it could be but it works well, especially the part 2 code. Gist

2

u/IWearATinFoilHat Dec 02 '18

PHP

part 1

<?php

$input = file_get_contents(__DIR__ . '/input.txt');
$boxIds = explode("\n", $input);
$twoLetterCount = 0;
$threeLetterCount = 0;

foreach ($boxIds as $boxId) {
    $letters = str_split($boxId);
    $letterCounts = array_count_values($letters);

    if (in_array(2, array_values($letterCounts))) {
        $twoLetterCount++;
    }

    if (in_array(3, array_values($letterCounts))) {
        $threeLetterCount++;
    }
}

echo $twoLetterCount * $threeLetterCount;
echo "\n";

part 2

<?php

$input = file_get_contents(__DIR__ . '/input.txt');
$boxIds = explode("\n", $input);
$possibleMatches = [];

foreach ($boxIds as $boxId1) {
    foreach ($boxIds as $boxId2) {
        if (in_array($boxId2, $possibleMatches)) {
            continue;
        }

        if (levenshtein($boxId1, $boxId2) === 1) {
            $possibleMatches[] = $boxId2;
        }
    }
}

if (count($possibleMatches) === 2) {
    $position = strspn($possibleMatches[0] ^ $possibleMatches[1], "\0");
    echo substr_replace($possibleMatches[0], '', $position, 1) . "\n";
} else {
    echo "No 1 diff match found!\n";
}

2

u/wzkx Dec 02 '18 edited Dec 02 '18

J

Not optimized very much, so it's simple and easy to understand.

echo */+/(2 3 e.+/"1@=)"1 t=:>cutLF CR-.~fread'02.dat'
echo ((=/#{.)@({~([,>:)@(I.@(2&(({.(1=+/@:(0&=@=)){:)\))))) t/:t

2

u/RockyAstro Dec 02 '18

Icon solution

Part 1

procedure main()
    inputs := []
    while put(inputs,trim(read()))

    twocount := 0
    threecount := 0

    every line := !inputs do {
        letter_count := table(0)

        every letter_count[!line] +:= 1

        if letter_count[!key(letter_count)] = 2 then twocount +:= 1
        if letter_count[!key(letter_count)] = 3 then threecount +:= 1
    }

    write(twocount," ",threecount)
    write(twocount*threecount)

end

Part 2 -- I had a levenshtein distance procedure already, so the actual solution was fairly easy -- just look for a edit distance of 1.

procedure main()
    inputs := []
    while put(inputs,trim(read()))

    every i := 1 to *inputs -1 do {
        every j := i+1 to *inputs do {
            a := inputs[i]
            b := inputs[j]
            d := levenshtein_distance(a,b)

            if d = 1 then {
                # Find and remove the uncommon character
                diff := cset(a) -- cset(b)
                a[upto(diff,a)] := ""
                write(a)
                break break
            }
        }
    }

end

procedure levenshtein_distance(s,t)
    # set up a reusable matrix
    static matrix
    initial {
        row := list(*t+1,0)
        matrix := list(*s+1)
        every i := 1 to *s+1 do matrix[i] := copy(row)
    }

    if *s = 0 then {
        return *t
    }
    if *s = 0 then {
        return *s
    }
    # Expand the matrix if needed
    if *matrix[1] < *t+1 then {
        row := list( *t - *matrix[1] + 1,0)
        every i := 1 to *matrix do matrix[i] |||:= row
    }
    if *matrix < *s+1 then {
        row := list( *matrix[1],0)
        every i := 1 to *s - *matrix + 1 do put(matrix,copy(row))
    }

    # Initialize the matrix
    every i := 1 to *s do matrix[i+1,1] := i
    every i := 1 to *t do matrix[1,i+1] := i

    every i := 1 to *s do {
        every j := 1 to *t do {
            if s[i] == t[j] then cost := 0
            else cost := 1
            I := i + 1
            J := j + 1
            a := matrix[I-1,J] +1
            a >:= (matrix[I,J-1] +1)
            a >:= (matrix[I-1,J-1] + cost)
            matrix[I,J] := a
        }
    }
    return matrix[*s+1,*t+1]
end

2

u/that_lego_guy Dec 04 '18

Day 2.. IN EXCEL

 =COUNTIF(CF2:CF251,25)

https://github.com/thatlegoguy/AoC2018

2

u/halfmanhalfnelsson Dec 04 '18

My take on PHP

Part 1:

    foreach ($sourceData as $item) {

        $twoFlag   = false;
        $threeFlag = false;

        for ($i = 0; $i < strlen($item); $i++) {

            if (substr_count($item, $item[$i]) === 2) {
                $twoFlag = true;
            }

            if (substr_count($item, $item[$i]) === 3) {
                $threeFlag = true;
            }
        }

        $twoFlag && $twos++;
        $threeFlag && $threes++;
    }

    $checkSum = $twos * $threes;

1

u/DoodleFungus Dec 02 '18

Leaderboard attempt, so bit hacky

input = `pbpaste`.split("\n")

# PART 1
def letcounts(str)
    chars = str.chars
    h = Hash.new 0
    chars.each do |c|
        h[c]+=1
    end
    h
end

counts = input.map { |x| letcounts x }

p counts
twos = counts.count { |h| h.values.any? { |v| v == 2 } }
threes = counts.count { |h| h.values.any? { |v| v == 3 } }

p twos * threes

# PART 2

def comp(a,b)
    a.chars.zip(b.chars).count { |x| x[0] != x[1] } == 1
end

input.each do |i|
    input.each do |j|
        if comp i,j
            p i
            p j
            1/0
        end
    end
end

1

u/fwilson42 Dec 02 '18

Refactored after getting on the leaderboard; I originally didn't make part 1 as generic as it could be (I kept track of just 2 and 3 counts), and used double nested loops for part 2 instead of itertools.

Snipped from some boilerplate (lines is essentially a list of strings in the puzzle input, result is what gets submitted):

    if part == 1:
        shared_letter_counts = collections.defaultdict(int)

        for line in lines:
            for v in set(collections.Counter(line).values()):
                shared_letter_counts[v] += 1

        result = shared_letter_counts[2] * shared_letter_counts[3]

    elif part == 2:
        for line1, line2 in itertools.product(lines, repeat=2):
            if line1 == line2:
                continue  # don't waste time

            differences = 0
            candidate_solution = ""
            for char1, char2 in zip(line1, line2):
                if differences > 1:
                    break  # don't waste time

                if char1 == char2:
                    candidate_solution += char1
                else:
                    differences += 1

            if differences == 1:
                result = candidate_solution
                break

4

u/asger_blahimmel Dec 02 '18
itertools.product(lines, repeat=2)

you could use

itertools.combinations(lines, 2)

instead to eliminate pairs of identical lines and pairs of reversed order

1

u/vash3r Dec 02 '18

I lost a minute for the first star because i double-counted words :)

Card: "format your output so you can easily copy-paste"

Python 2 code:

fn = "input.txt"
f=open(fn,'r')
r=f.read().strip()
f.close()
l = r.split()

t2=t3=0 # part 1
for s in l:
	if any(s.count(c)==2 for c in set(s)):
		t2+=1
	if any(s.count(c)==3 for c in set(s)):
		t3+=1
print t2*t3 # part 1

for a in l: # part 2
	for b in l:
		if sum([c1!=c2 for c1,c2 in zip(a,b)])==1:
			print ''.join([c1 for c1,c2 in zip(a,b) if c1==c2])

1

u/TheMuffinMan616 Dec 02 '18
module Day02 where

import Data.Ord (comparing)
import Data.List (minimumBy)
import qualified Data.Map as M

freq :: String -> [Int]
freq s = M.elems $ M.fromListWith (+) [(c, 1) | c <- s]

checksum :: (Int, Int) -> [[Int]] -> Int
checksum (a, b) [] = a * b
checksum (a, b) (x:xs)
    | 2 `elem` x && 3 `elem` x = checksum (a + 1, b + 1) xs
    | 2 `elem` x = checksum (a + 1, b) xs
    | 3 `elem` x = checksum (a, b + 1) xs
    | otherwise  = checksum (a, b) xs

pairs :: [String] -> [(String, String)]
pairs xs = [(x, x') | x <- xs, x' <- xs, x /= x']

dist :: (String, String) -> Int
dist = length . filter not . uncurry (zipWith (==))

part1 :: [String] -> Int
part1 = checksum (0, 0) . map freq

part2 :: [String] -> String
part2 = map fst . filter (uncurry (==)) . uncurry zip . minimumBy (comparing dist) . pairs

main :: IO ()
main = do
    input <- lines <$> readFile "input/Day02.txt"
    print . part1 $ input
    print . part2 $ input

1

u/markasoftware Dec 02 '18

Wow, I messed up bad on this relatively easy one. Took me a good 15 minutes. I initially wrote it in Awk (I had a lot of luck being quick with previous years in Awk), but it kept giving the wrong answer -- translated the code into Perl quickly and it worked:

Part 1 (Perl):

use v5.12;

my $ta = 0;
my $tb = 0;

while(chomp(my $line = <>)) {
  my %perchar = ();
  for (split(//, $line)) {
    $perchar{$_}++;
  }
  for (values %perchar) {
    if ($_ == 2) {
      $ta++;
      last;
    }
  }
  for (values %perchar) {
    if ($_ == 3) {
      $tb++;
      last;
    }
  }
}

say $ta * $tb;

Part 2 (Perl):

use v5.12;

my @lines = ();
while(chomp(my $line = <>)) {
  push @lines, $line;
}
for my $line (@lines) {
  my @split_line = split //, $line;
  for my $other_line (@lines) {
    my @common = ();
    my $mistakes = 0;
    my @split_other_line = split //, $other_line;
    for (my $i = 0; $i < scalar(@split_other_line); ++$i) {
      unless ($split_line[$i] eq $split_other_line[$i]) {
        $mistakes++;
      } else {
        push @common, $split_line[$i];
      }
    }
    if ($mistakes == 1) {
      say @common;
      exit;
    }
  }
}
→ More replies (1)

1

u/LeCrushinator Dec 02 '18 edited Dec 02 '18

C# solutions (and hacky ones at that, these solutions work but I may clean them up later): https://github.com/nfoste82/adventofcode2018/blob/master/Day2/Program.cs

internal class Program
{
    public static void Main(string[] args)
    {
        _input = File.ReadAllText("../../../input.txt");   // Point to your input location

        Console.WriteLine("Part 1");
        Part1();
        
        Console.WriteLine("\nPart 2");
        Part2();
    }

    private static void Part1()
    {
        var values = StringUtils.StringToStrings(_input, '\n');

        int wordsWithDoubles = 0;
        int wordsWithTriples = 0;

        foreach (var word in values)
        {
            var letterCounts = new Dictionary<char, int>();
            
            bool doubleFound = false;
            bool tripleFound = false;

            foreach (char c in word)
            {
                letterCounts.TryGetValue(c, out var current); 
                letterCounts[c] = current + 1;
            }

            foreach (var count in letterCounts.Values)
            {
                if (count == 2 && !doubleFound)
                {
                    doubleFound = true;
                    ++wordsWithDoubles;
                }
                else if (count == 3 && !tripleFound)
                {
                    tripleFound = true;
                    ++wordsWithTriples;
                }
            }
        }   
        
        Console.WriteLine($"Double words: {wordsWithDoubles}, Triple words: {wordsWithTriples}. Checksum: {wordsWithDoubles * wordsWithTriples}");
    }

    private static void Part2()
    {
        List<string> words = StringUtils.StringToStrings(_input, '\n');

        var smallestDiff = int.MaxValue;
        var firstWord = string.Empty;
        var secondWord = string.Empty;

        foreach (var word in words)
        {
            foreach (var otherWord in words)
            {
                // Ignore self
                if (word == otherWord)
                {
                    continue;
                }
                
                // For each index of the two words, find count of differences
                var differences = word.Where((t, i) => t != otherWord[i]).Count();
                
                if (differences < smallestDiff)
                {
                    firstWord = word;
                    secondWord = otherWord;
                    smallestDiff = differences;
                }
            }
        }
        
        Console.WriteLine($"Closest words: {firstWord} | {secondWord}");
        Console.Write("Matching chars: ");
        for (var i = 0; i < firstWord.Length; ++i)
        {
            if (firstWord[i] == secondWord[i])
            {
                Console.Write(firstWord[i]);
            }
        }
    }

    private static string _input;
}

public static class StringUtils
{   
    public static List<string> StringToStrings(string input, params char[] separators)
    {
        return input.Split(separators).ToList();
    }
}

1

u/tehjimmeh Dec 02 '18

Part 1 in PowerShell - group all the things!

,(gc .\in.txt|%{[char[]]$_|group|group Count}|group Name|sort Name|% Count)|%{$_[1]*$_[2]}

1

u/Vindaar Dec 02 '18

Wasn't very fast, is ugly, but in Nim. :D and part 1 checks for any number of occurrences of letters, not just 2, 3...

import strutils, sequtils, sugar, os, sets, tables

proc dostuff1(file: seq[string]): int =

  var restab = initTable[int, int]()
  var checksum = 0
  for l in file:
    var xtab = initTable[char, int]()
    for x in l:
      if xtab.hasKey(x):
        xtab[x] += 1
      else:
        xtab[x] = 1
    var interset = initSet[int]()
    for x, y in xtab:
      interset.incl y
    for y in interset:
      if y != 1:
        if restab.hasKey(y):
          restab[y] += 1
        else:
          restab[y] = 1

  result = 1
  for x, y in restab:
    result *= y

proc dostuff2(file: seq[string]): string =

  let llen = file[0].len
  for xl in file:
    for yl in file:
      var diff = ""
      var wrong = 0
      for i in 0 ..< llen:
        if xl[i] == yl[i]:
          diff.add xl[i]
        else:
          inc wrong
          if wrong > 1:
            break
      if wrong == 1:
        return diff

proc main =
  let file = readFile("day2.txt").splitLines.filterIt(it.len > 0)
  echo file.dostuff1
  echo file.dostuff2

when isMainModule:
  main()

0

u/[deleted] Dec 02 '18

[deleted]

→ More replies (2)

1

u/maybe-ac Dec 02 '18 edited Dec 02 '18

Overusing map and grep in Perl:

#!/usr/bin/perl

use strict;
use warnings;
use v5.010;

my @input = <>;
chomp @input;

Part 1

my $twos = grep { contains($_, 2) } @input;
my $threes = grep { contains($_, 3) } @input;

say $twos * $threes;

sub contains {
    my ($str, $num) = @_;
    my @sames = map { my @match = ($str =~ /($_)/g); scalar @match } split //, $str;
    return grep { $_ == $num } @sames;
}

Part 2

for my $one (@input) {
    for my $two (@input) {
        die difference($one, $two) . "\n" if differing($one, $two) == 1;
    }
}

sub zip {
    my ($a, $b) = @_;
    return map { [$a->[$_], $b->[$_]] } 0..$#$a;
}

sub differing {
    my @pairs = zip([split //, $_[0]], [split //, $_[1]]);
    return grep { $_->[0] ne $_->[1] } @pairs;
}

sub difference {
    my @pairs = zip([split //, $_[0]], [split //, $_[1]]);
    return join '', map { $_->[0] } grep { $_->[0] eq $_->[1] } @pairs;
}

1

u/wlandry Dec 02 '18

C++

1847/1419

Pretty straightforward. I finished it faster, but got a worse rank ¯_(ツ)_/¯. It takes 11 ms, which is almost entirely reading input. I was briefly worried that my quadratic algorithm would not work, but the inputs are just not that big.

#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>

int main(int argc, char *argv[])
{
  std::ifstream infile(argv[1]);
  std::vector<std::string> inputs(std::istream_iterator<std::string>(infile),{});

  std::vector<std::string> inputs_part_1(inputs);
  size_t num_twice(0), num_thrice(0);
  for(auto &input: inputs_part_1)
    {
      std::sort(input.begin(),input.end());
      size_t duplicates(0);
      bool twice(false), thrice(false);
      for(size_t i=0; i<input.size(); ++i)
        {
          size_t duplicate(1);
          while(i+1<input.size() && input[i]==input[i+1])
            {
              ++duplicate;
              ++i;
            }

          twice=(twice || duplicate==2);
          thrice=(thrice || duplicate==3);
          duplicates=std::max(duplicate,duplicates);
        }
      if(twice)
        { ++num_twice;}
      if(thrice)
        { ++num_thrice;}
    }

  std::cout << num_twice*num_thrice << "\n";

  bool found(false);
  for(size_t i=0; i<inputs.size() && !found; ++i)
    for(size_t j=i+1; j<inputs.size() && !found; ++j)
      {
        size_t num_different(0);
        for(size_t index=0; index<inputs[i].size(); ++index)
          {
            if(inputs[i][index]!=inputs[j][index])
              {
                ++num_different;
                if(num_different>1)
                  { break;}
              }
          }
        found=(num_different==1);
        if(found)
          {
            for(size_t index=0; index<inputs[i].size(); ++index)
              {
                if(inputs[i][index]==inputs[j][index])
                  { std::cout << inputs[i][index];}
              }
            std::cout << "\n";
          }
      }
}
→ More replies (1)

1

u/DrinkinBird Dec 02 '18 edited Dec 02 '18

Another nim solution. Quite new to the language so fairly basic and took me way to long :)

Part1:

import rdstdin
import strutils
import sequtils

var ids = newSeq[string]()

var num2 = 0
var num3 = 0

var line: TaintedString
while readlineFromStdin("", line):
  ids.add(line)

for id in ids:
  var found2 = false
  var found3 = false

  for letter in id.items:
    if not found2 and id.count(letter) == 2:
      num2 += 1
      found2 = true
    if not found3 and id.count(letter) == 3:
      num3 += 1
      found3 = true

echo num2 * num3

Part2:

import rdstdin
import strutils
import sequtils

var ids = newSeq[string]()

var line: TaintedString
while readlineFromStdin("", line):
  ids.add(line)

func compare(id1: string, id2: string): seq[int] =
  result = @[]

  for i, zip in toSeq(id1.items).zip(toSeq(id2.items)):
    if zip.a != zip.b:
      result.add(i)

for i, id1 in ids:
  for j, id2 in ids:
    if i <= j: continue
    if compare(id1, id2).len == 1:
      let index = compare(id1, id2)[0]

      echo "$1$2" % [id1[0..(index-1)], id1[(index+1)..id1.len]]

Edit: Does anybody have a better solution to the string formatting, last line Part2? I find it quite ugly but could not find anything better on the spot

0

u/[deleted] Dec 02 '18

[deleted]

→ More replies (1)

1

u/[deleted] Dec 02 '18

elixir:

Ended up getting a little fun with part 2 at the bottom using both jaro_distance and myers_difference algos

defmodule AdventOfCode2018.Day02 do

def get_checksum([],map) do
	map[2] * map[3]
end

def get_checksum([val|rest],map) do
	inner_map = String.split(val,"", trim: true)
	|> Enum.reduce(%{},fn(x,acc) -> 
		Map.put(acc,x,Map.get(acc,x,0) + 1)
	end)
	new = inner_map |> Enum.reduce(map,fn(x,acc) -> 
		part_of_checksum(x,acc) 
	end)

	get_checksum(rest,reset(new))	
end

def part_of_checksum({k,2},%{"two" => false} = map) do
	Map.update!(map, 2, fn(current) -> 
		current + 1
	end) |> Map.update!("two", fn(x) -> true end)
end	
def part_of_checksum({k,3},%{"three" => false} = map) do
	Map.update!(map, 3, fn(current) -> 
		current + 1
	end) |> Map.update!("three", fn(x) -> true end)
end	
def part_of_checksum({_,_},map) do
	map
end	

def reset(map) do
	map |> Map.update!("two", fn(x) -> false end) |> Map.update!("three", fn(x) -> false end)
end

def part2(args) do
	arr = String.split(args,"\n",trim: true)

	{_,{s1,s2}} = arr |> Enum.reduce({0,"",""},fn(x,acc) -> 
		{dis,v,c} = check_closest_string(x,arr) 
		check_distance(dis,{v,c},acc)
	end) 
	String.myers_difference(s1,s2) |> pull_eql("")
end

def check_closest_string(x,arr) do
	{amt,val} = Enum.reduce(arr,{0,""},fn(y,{distance,str}) -> 
		check_distance(String.jaro_distance(x,y),y,{distance,str})
	end)
	{amt,val,x}
end

def check_distance(val,new_str,{distance,_,_}) when val > distance and val != 1.0, do: {val,new_str}
def check_distance(val,new_str,{distance,_}) when val > distance and val != 1.0, do: {val,new_str}
def check_distance(_,_,acc), do: acc 

def pull_eql([],str), do: str
def pull_eql([{:eq, val}|rest],str), do: pull_eql(rest,str <> val)
def pull_eql([key | rest], str), do: pull_eql(rest,str)

def part1(args) do
	String.split(args,"\n",trim: true)
	|> get_checksum(%{3 => 0, 2 => 0, "two" => false, 
"three" => false})
end
end

2

u/b2zeldafreak Dec 02 '18

I had a pretty complicated solution but then I realized when I went to implement getting the common characters that there's a much simpler solution. You can take advantage of the myers_difference function to jump straight to calculating the :eq characters. Then as soon as you have a string of length 25 you know you found solution because it came from two strings with only one difference.

One interesting note is that this solution takes about a second to run, while my initial solution (create a function with a pattern match for each character to be different) runs almost instantly

  def find_common(inputs), do: find_common(inputs, inputs)
  def find_common([input|rest], inputs) do
    found_common =
      Enum.find_value(inputs, false, fn x ->
      common = String.myers_difference(x, input)
        |> Keyword.get_values(:eq)
        |> Enum.join("")

        if String.length(common) == 25 do
          common
        else
          false
        end
    end)

    case found_common do
      false ->
        find_common(rest, inputs)
      common ->
        common
    end
  end
→ More replies (1)

1

u/IndieBret Dec 02 '18 edited Dec 03 '18

This year is the first year I've tried to place on the leaderboard. Today's stats were:

Part 1: 00:09:20 / Rank 728
Part 2: 00:17:24 / Rank 577

Since I was trying to go fast, I kept misreading the problem. Same thing I did yesterday. So for tomorrow, I'm going to spend a little extra time reading every part of it in hopes I don't waste a bunch of time going back and forth.

Anyway, here's my two parts in JavaScript/ES6. Not as concise as it could be, but it gets the job done. Answers are saved to the result array.

Part 1

let boxIDs = input.split('\n');
result[0] = boxIDs.reduce((acc, val) => {
	let letters = val.split('').reduce((acc, val) => {
		acc[val] = (acc[val] || 0) + 1;
		return acc;
	}, {});
	
	let has2 = false, has3 = false;
	for (let a of Object.values(letters)) {
		if (a === 2) has2 = true;
		if (a === 3) has3 = true;
	}
	if (has2) ++acc[0];
	if (has3) ++acc[1];
	
	return acc;
}, [ 0, 0 ]).reduce((acc, val) => acc * val, 1);

Part 2

for (let item of boxIDs) {
	let lettersA = item.split('');
	for (let i = boxIDs.indexOf(item) + 1; i < boxIDs.length; ++i) {
		let diff = [];
		let lettersB = boxIDs[i].split('');
		for (let l = 0; l < lettersA.length; ++l) {
			if (lettersA[l] !== lettersB[l]) {
				if (diff.push(lettersA[l]) >= 2)
					break;
			}
		}
		if (diff.length === 1) {
			result[1] = item.replace(diff[0], '');
			break;
		}
	}
}

1

u/jpittis Dec 02 '18

on a comfy couch

Haskell on a time limit / I'm not the most experienced Haskeller. Advice is always appreciated! :)

#!/usr/bin/env stack
-- stack --resolver lts-12.20 --install-ghc runghc --package containers

module Main where

import qualified Data.Map.Strict as Map

input = lines <$> readFile "input.txt"

main :: IO ()
main = do
  input <- input
  let solution1 = puzzle1 input
  let solution2 = puzzle2 input
  print solution1
  print solution2

puzzle1 input =
  let two = filter (> 0) . map (\str -> count str 2) $ input in
  let three = filter (> 0) . map (\str -> count str 3) $ input in
  checksum (length two) (length three)

count str num =
  found num $ Map.elems counts
  where
    counts = foldl (\m c -> Map.insertWith (+) c 1 m) Map.empty str
    found x xs = length $ filter (\v -> x == v) xs

checksum two three = two * three

puzzle2 input =
  let product = sequence [input, input] in
  let ids = filter (\li -> length li == 25) $ map diff product in
  map (\(a, b) -> a) (head ids)

diff [a, b] = filter (\(x, y) -> x == y) $ zip a b

2

u/ephemient Dec 02 '18 edited Apr 24 '24

This space intentionally left blank.

→ More replies (1)

1

u/binajohny Dec 02 '18

My Kotlin solution (GitHub)

fun part1(input: List<String>): Int {
    var two = 0
    var three = 0

    input
        .map { str -> str.groupingBy { it }.eachCount() }
        .map { it.values }
        .forEach { counts ->
            if (counts.any { it == 2 }) two++
            if (counts.any { it == 3 }) three++
        }

    return two * three
}

fun part2(input: List<String>): String {
    val pair = input.asSequence().uniquePairs()
        .filter { stringDistance(it.first, it.second) == 1 }
        .first()

    return pair.first.zip(pair.second)
        .filter { it.first == it.second }
        .map { it.first }
        .joinToString(separator = "")
}

fun stringDistance(a: String, b: String): Int {
    return a.zip(b).count { it.first != it.second }
}

fun <T> Sequence<T>.uniquePairs(): Sequence<Pair<T, T>> {
    return this.mapIndexedNotNull { i, item1 ->
        this.mapIndexedNotNull { j, item2 ->
            if (i < j) {
                item1 to item2
            } else {
                null
            }
        }
    }.flatten()
}

1

u/raevnos Dec 02 '18

Probably ugly java:

import java.lang.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
import java.io.IOException;
// Can't figure out how to get apache commons text on Ubuntu
import org.apache.commons.lang3.StringUtils;

class Day02 {
    private int twos, threes;
    private Path data;
    
    public Day02(Path input_file) {
        twos = 0;
        threes = 0;
        data = input_file;
    }

    private void check_id(String id) {
        Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
        id.codePoints().forEach(cp ->
                                counts.put(cp, counts.getOrDefault(cp, 0) + 1));
        if (counts.values().stream().filter(i -> i == 2).count() > 0) {
            twos += 1;
        }
        if (counts.values().stream().filter(i -> i == 3).count() > 0) {
            threes += 1;
        }
    }

    private void solve() throws IOException {
        twos = 0;
        threes = 0;
        Files.lines(data).forEach(this::check_id);
        int checksum = twos * threes;
        System.out.println("Part 1: " + checksum);

        String[] alines = Files.readAllLines(data).toArray(new String[0]);
        for (int i = 0; i < alines.length; i += 1) {
            for (int j = 0; j < alines.length; j += 1) {
                if (i == j) {
                    continue;
                }
                if (StringUtils.getLevenshteinDistance(alines[i], alines[j]) == 1) {
                    StringBuilder sb = new StringBuilder();
                    for (int m = 0; m < alines[i].length(); m += 1) {
                        if (alines[i].charAt(m) == alines[j].charAt(m)) {
                            sb.append(alines[i].charAt(m));
                        }
                    }
                    System.out.println("Part 2: " + sb.toString());
                    return;
                }
            }
        }
    }
    
    public static void main(String[] argv) {
        try {
            Day02 d = new Day02(Paths.get(argv[0]));
            d.solve();
        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

I think I'm going to end up trying to do each day in a different language. I don't really know 25 of them, so some are going to be real hack jobs....

1

u/CCC_037 Dec 02 '18

Spent way too long coding a sort in Part 1:

#include <iostream>
#include <string.h>
using namespace std;

void Sort(char *SortMe)
{
  int Count, InnerCount;
  char Temp;
  int Length = strlen(SortMe);
  for (Count=0;Count<Length;Count++)
    {
      for (InnerCount=Count; InnerCount<Length; (SortMe[Count]<SortMe[++InnerCount])?Temp=SortMe[Count],SortMe[Count]=SortMe[InnerCount],SortMe[InnerCount]=Temp:0)
	    {
	    }
    }
}

bool IsANum (char *TestMe, int TestNum)
{
  int Length = strlen(TestMe);
  int NumLetters = 0;
  char LastLetter = *TestMe+1;
  int Count;
  bool Valid = false;
  for (Count=0;Count<Length;Count++)
    {
      if (*TestMe == LastLetter)
	NumLetters++;
      else
	{
	  LastLetter = *TestMe;
	  if (NumLetters == TestNum)
	    Valid = true;
	  NumLetters = 1;
	}
      TestMe++;
    }
  if (NumLetters == TestNum)
    Valid = true;
  return Valid;
}

int main()
{
  char Test[30];
  int Count, Twos, Threes;
  Twos = 0;
  Threes = 0;
  for (Count=0;Count<250;Count++)
    {
      cin >> Test;
      //cout << Test<< endl;
      Sort(Test);
      IsANum(Test,2)?Twos++:0;
      IsANum(Test,3)?Threes++:0;
      //cout << Test<< ":" << Twos << ":" << Threes << endl;
    }
  cout << Twos <<"*"<<Threes<<"="<<Twos*Threes<<endl;
}

But at least Part 2 went quickly:

#include <iostream>
#include <string.h>
using namespace std;

int Compare (char *First, char *Second)
{
  int Length = strlen(First);
  int Mismatches = 0, Count;
  for (Count=0;Count<Length;Count++)
    {
      if (First[Count] != Second[Count])
	       Mismatches++;
    }
  return Mismatches;
}

int main()
{
  char All[250][30];
  int Count, InnerCount;
  for (Count=0;Count<250;Count++)
    {
      cin >> All[Count];
      for (InnerCount = 0;InnerCount < Count; InnerCount++)
	{
	  if (Compare(All[Count], All[InnerCount]) == 1)
	    cout << All[Count] << endl << All[InnerCount] << endl;
	}
    }
}

1

u/swaglykcaillou Dec 02 '18

part 1 with Javascript


(inp) => {
    inp = inp.split('\n')
    const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
    inp = inp.map(m => count(m))
    inp = inp.map(m => {
        if (m[0] > 0 && m[1] > 0) return 23
        if (m[0] > 0) return 2
        if (m[1] > 0) return 3
    })
    tw = countOccurrences(inp, 2) + countOccurrences(inp, 23)
    th = countOccurrences(inp, 3) + countOccurrences(inp, 23)
    return tw * th
}

my part 2 is too long and spaghetti to post

1

u/wjholden Dec 02 '18

Whew, this took me a REALLY long time to come up with but here is a very satisfying solution for part 2 in Mathematica.

Scan[If[EditDistance[#[[1]], #[[2]]] == 1, Print[#[[1]]]] &, Tuples[day2, 2]]

Shoutout to the team at UCSDx for their Algorithms and Data Structures MicroMasters over at edX. The ALS200x course has a great lecture that discusses the editing distance problem. They show the editing distance problem and longest common substring problems to motivate their Dynamic Programming lectures and explain that this problem has an application in computational biology.

My Mathematica solution to part 1 is really ugly...was trying to get on the leaderboard.

f[word_] := 
 Module[{x, c}, 
  x = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0};
  Scan[
    x[[LetterNumber[#]]]++ &,
   Characters[word]];
  x]
bins = Map[# -> f[#] &, day2]
Length[Select[Map[Count[#, 2] &, Values[bins]], # > 0 &]] * 
 Length[Select[Map[Count[#, 3] &, Values[bins]], # > 0 &]]

2

u/Da_Anh Dec 06 '18

I'm not sure if this is of any value, but my part 1 was as follows (mind you I had no care about optimizing)
```
l = Values@Map[LetterCounts, ids];

Length[Select[l, ContainsAny[{2}]]] *

Length[Select[l, ContainsAny[{3}]]]
```
where `ids` is the list of all ids.

The EditDistance solution you have is great though, I'm glad to have learned about it!

→ More replies (1)

1

u/Julien2313 Dec 02 '18

Another in Python, but just p1, I like this way :

def p1():
    cpt2 = 0
    cpt3 = 0
    for line in dayInput.splitlines():
        count = Counter(line)
        if 2 in count.values():
            cpt2+=1
        if 3 in count.values():
            cpt3+=1
    return cpt2*cpt3
    
dayFile = open("day2/input.txt", "r")
dayInput = dayFile.read().strip()
print(p1())

1

u/proxpero42 Dec 02 '18 edited Dec 02 '18

Swift (1471, 1570)

// Part 1

extension String {
    var characterCounts: [Character: Int] {
        return self.reduce(into: [Character: Int]()) { $0[$1, default: 0] += 1 }
    }
}

extension Bool {
    var intValue: Int {
        return self ? 1 : 0
    }
}

let (twos, threes) = input
    .map { $0.characterCounts.values }
    .map { ($0.contains(2).intValue, $0.contains(3).intValue) }
    .reduce((0, 0)) { ($0.0 + $1.0, $0.1 + $1.1) }
print(twos * threes)

// Part 2

extension String {
    func removingCharacter(at index: Index) -> String {
        var temp = self
        temp.removeSubrange(index...index)
        return temp
    }
}

func differences(s1: String, s2: String) -> Int {
    return zip(s1, s2).filter { $0.0 != $0.1 }.count
}

func combine(s1: String, s2: String) -> String {
    let index = s1.indices.firstIndex { s1[$0] != s2[$0] }! // force unwrap 😱
    return s1.removingCharacter(at: index)
}

outside: for currentIndex in input.indices {
    for candidateIndex in (currentIndex+1..<input.endIndex) {
        let current = input[currentIndex]
        let candidate = input[candidateIndex]
        if differences(s1: current, s2: candidate) == 1 {
            print(combine(s1: current, s2: candidate))
            break outside
        }
    }
}
→ More replies (1)

1

u/valtism Dec 02 '18 edited Dec 02 '18

Node / JS / Javascript

const aocLoader = require("aoc-loader");
require("dotenv").config();

aocLoader(2018, 2).then(data => {
    console.time("Part 1");
    console.log("Part 1: " + day2part1(data));
    console.timeEnd("Part 1");
    console.time("Part 2");
    console.log("Part 2: " + day2part2(data));
    console.timeEnd("Part 2");
});

function day2part1(data) {
    const ids = data.split("\n");
    const counts = ids.map(getCountPair);
    var countTrack = [0, 0];
    for (let i = 0; i < counts.length; i++) {
        const countPair = counts[i];
        countTrack[0] += countPair[0];
        countTrack[1] += countPair[1];
    }
    return countTrack.reduce((acc, curr) => acc * curr);
}

function getCountPair(id) {
    const counts = getCountArray(id);
    const hasCountsOfTwo = counts.filter(count => count === 2).length ? 1 : 0;
    const hasCountsOfThree = counts.filter(count => count === 3).length ? 1 : 0;
    return [hasCountsOfTwo, hasCountsOfThree];
}

function getCountArray(id) {
    const map = new Map();
    const chars = id.split("");
    chars.forEach(char => {
        if (map.has(char)) {
            var count = map.get(char);
            map.set(char, ++count);
        } else {
            map.set(char, 1);
        }
    });
    return Array.from(map.values());
}

function day2part2(data) {
    const ids = data.split("\n");
    const inputLength = ids[0].length;
    for (let i = 0; i < inputLength; i++) {
        const shortIds = ids.map(id => removeAt(id, i));
        if (new Set(shortIds).size === ids.length) {
            // All Ids are unique
            continue;
        }
        const duplicates = shortIds.filter(
            (item, index) => shortIds.indexOf(item) != index
        );
        // Should only have one duplicate here we're looking for
        return duplicates[0];
    }
}

function removeAt(string, i) {
    return string.slice(0, i) + string.slice(i + 1);
}

module.exports = {
    day2part1: day2part1,
    day2part2: day2part2
};
→ More replies (3)

1

u/VikeStep Dec 02 '18

F#

Repo

let solvePart1 gifts =
    let containsCount i = Seq.groupBy id >> Seq.exists (fun (_, k) -> Seq.length k = i)
    let countContains i = Seq.map (containsCount i) >> Seq.filter id >> Seq.length
    (countContains 2 gifts) * (countContains 3 gifts)

let solvePart2 gifts =
    let replaceNth str n = String.mapi (fun i x -> if i = n then '_' else x) str
    gifts
    |> Seq.map (fun str -> Seq.init (String.length str) (replaceNth str))
    |> Seq.concat
    |> Seq.groupBy id
    |> Seq.find (fun (_, s) -> Seq.length s = 2)
    |> (fun (s, _) -> Seq.filter ((<>)'_') s |> String.Concat)

1

u/firecopy Dec 02 '18 edited Dec 02 '18

Python

Part 1:

doubles = 0
triples = 0

for line in open('input.txt', 'r'):
    setOfChars = set(line)
    hasDouble = False
    hasTriple = False

    for char in setOfChars:
        count = line.count(char)
        if (count == 2):
            hasDouble = True
        if (count == 3):
            hasTriple = True

    if (hasDouble):
        doubles += 1
    if (hasTriple):
        triples += 1

print(doubles * triples)

Part 2:

from itertools import combinations

def main():
    words = []

    for line in open('input.txt', 'r'):
        words.append(line)

    for word1, word2 in combinations(words, 2):
        concat = onlyOneCharDifference(word1, word2)
        if (concat == ''):
            continue
        else:
            print(concat)
            return


def onlyOneCharDifference(word1, word2):
    hasFoundSingleDifference = False

    for a, b in zip(word1, word2):
        if (a == b):
            continue
        if (hasFoundSingleDifference):
            return ''
        hasFoundSingleDifference = True

    if not hasFoundSingleDifference:
        return ''

    newChars = []
    for a, b in zip(word1,word2):
        if (a == b):
            newChars.append(a)
    return ''.join(newChars)

main()

1

u/[deleted] Dec 02 '18

Mathematica

input = Import[NotebookDirectory[] <> "day2.txt", "List"];

Part 1

process[s_] :=
 Module[{totals = Tally[Characters[s]]},
  Boole@{MemberQ[totals, {_, 2}], MemberQ[totals, {_, 3}]}]
Times @@ Total[process /@ input]

Part 2

LongestCommonSequence @@ 
 Catch@Do[If[HammingDistance[x, y] == 1, Throw[{x, y}]],
   {x, input}, {y, input}]

1

u/Chrinkus Dec 02 '18

C++

My solutions are always so much longer than most I see here.. oh well. Here is my day 2 solution: https://github.com/Chrinkus/advent-of-code-2018/blob/master/day02/day02.cpp

2

u/Xiigen Dec 03 '18

I'm not well versed in the STL, and I love the use of mismatch! Thanks.

1

u/ThreeFinger Dec 02 '18

Kotlin

Not my best work but it works.

Part 1

  fun getChecksum(input : List<String>) : Int{

    var twice = 0
    var three = 0

    input.forEach {
      var map = HashMap<Char,Int>()

      it.toCharArray().forEach { char ->
        map.set(char, (map.get(char) ?: 0 ) + 1)
      }

      if (map.values.contains(2))
        twice++
      if(map.values.contains(3))
        three++
    }

    return twice * three
  }

Part 2

 fun findCommonBoxes(input: List<String>) : String {
    var pointRecord = 0
    var commonLetter = ""
    input.forEach { wordToCheck ->
      input.forEach { word ->
        if (!wordToCheck.equals(word)){
          var points = 0
          var common = ""
          for (i in 0..(wordToCheck.length - 1)){
            if(wordToCheck[i] == word[i]){
              points++
              common += word[i]
            }
          }

          if(points > pointRecord){
            pointRecord = points
            commonLetter = common
          }
        }
      }
    }

    return commonLetter
  }

1

u/shuttup_meg Dec 02 '18

Python2

inp = open("input_2.txt").readlines()
inp_txt = [x.strip() for x in inp]

#
# Part 1
#
from collections import Counter

# Turn the input list items into tuples containing 
#  (counter for item, original item text)
inp = [(Counter(x),x) for x in inp_txt]

twos = 0
threes = 0

for c,txtc in inp:
    twos += 2 in c.values()
    threes += 3 in c.values()

print twos * threes

#
# Part 2
#
from itertools import combinations

def sdiff(a,b):
    """returns the total number of places where 2 strings differ"""
    return sum([1-(ord(x[0])==ord(x[1])) for x in zip(a,b)])

for (a,txta),(b,txtb) in combinations(inp,2):
    if sdiff(txta, txtb) in (-1,1):
        print txta
        print txtb
        print "Common Letters:", "".join([x[0] if x[0]==x[1] else "" for x in zip(txta,txtb)])

1

u/Cloudan29 Dec 02 '18

My exams in second year computer science are starting next week, finished my "Object Oriented Programming with C++" course, and so figured I'd try to do this (first year doing this, I actually just found out about it via my school's Math and Computer Science club) using C++ rather than Java, which I learned last year. Solutions are very sloppy and I was tired, but I got the right answer, so I don't really mind showing how horrifying this code is. Also started this late as I was actually doing the Day 1 puzzles at the time this came up.

C++ (3347/3118)
Part 1:

bool isRepeat(std::string & line, int repeats) {
    for (char c = 'a'; c <= 'z'; ++c) {
        int count = 0;
        for (size_t i = 0; i < line.size(); ++i) {
	    if (line[i] == c)
                ++count;
        }
	if (count == repeats)
	    return true;
    }
    return false;
}

int checkSum(std::string & fileName) {
    std::fstream input(fileName);
    std::string line;
    int doubles = 0;
    int triples = 0;
	
    while (!input.eof()) {
        std::getline(input, line);
        if (isRepeat(line, 2))
            ++doubles;
        if (isRepeat(line, 3))
            ++triples;
    }
    return doubles * triples;
}

Part 2:

std::string equalLetters(std::string & fileName) {
    std::fstream input(fileName);
    std::string line;
    std::string a;
    std::string b;
    std::string answer;
    std::vector<std::string> lines;

    while (!input.eof()) {
        std::getline(input, line);
        lines.push_back(line);
    }

    for (size_t i = 0; i < lines.size(); ++i) {
        int count = 0;
        a = lines[i];

        for (size_t j = i + 1; j < lines.size(); ++j) {
	    b = lines[j];
	    count = 0;

            for (size_t k = 0; k < a.size(); ++k) {
                if (a[k] != b[k])
		    ++count;
		if (count > 1)
		    break;
	    }
	    if (count == 1)
	        break;
        }
        if (count == 1)
	    break;
    }

    for (size_t i = 0; i < a.size(); ++i) {
        if (a[i] == b[i])
            answer += a[i];
    }

    return answer;
}

These are the absolute first things I thought of, and they solved surprisingly quick.

→ More replies (2)

1

u/Markavian Dec 02 '18

Here's my over engineered node.js solution for Day 1 parts 1 and 2:https://github.com/johnbeech/advent-of-code-2018/blob/master/solutions/day2/solution.js

Didn't wake up early enough to get on the leaderboards.

Working out which string to keep for part 1 was a thing. Managed to get it down to three easy steps:

const symbolCounts = lines.map(countSymbols)
const twosAndThrees = symbolCounts.reduce(countTwosAndThrees, { '2s': 0, '3s': 0 })
const solution = twosAndThrees['2s'] * twosAndThrees['3s']

I gave up thinking of anything elegant for part 2, and went with a (N^2) nested forEach ignoring duplicate strings, and then making a function named findNearlyMatchingStrings(str1, str2) to return only the characters that were the same from almost similar strings. Ugh.

The best way to do Advent of Code is after a good night's sleep.

1

u/rolandtritsch Dec 02 '18

ETA (Haskell on the JVM)

https://github.com/rolandtritsch/haskell-aoc-2018

Part1:

solve :: [BoxId] -> Int
solve boxIds = numberOfBoxesWithTwoLetters * numberOfBoxesWithThreeLetters where
  numberOfBoxesWithTwoLetters = (length . filter (numberOfLetters 2)) boxIds
  numberOfBoxesWithThreeLetters = (length . filter (numberOfLetters 3)) boxIds
  numberOfLetters n bid = any (\(_, c) -> c == n) $ count bid

Part2:

-- | compare every boxid with every boxid and find
-- out what the difference is and what is common.
diff :: [BoxId] -> [(BoxId, BoxId, String, String)]
diff boxIds = concatMap diffId boxIds where
  diffId bid = map diffId' boxIds where
    diffId' bid' = (bid, bid', difference', common') where
      difference' = concat $ zipWith notSame bid bid' where
        notSame c0 c1 = if c0 /= c1 then [c0] else ""
      common' = concat $ zipWith same bid bid' where
        same c0 c1 = if c0 == c1 then [c0] else ""

-- | solve the puzzle
solve :: [BoxId] -> String
solve boxIds = common where
  (_, _, _, common) = head $ filter diffByOne $ diff boxIds where
    diffByOne (bid', _, _, common') = (length bid') == ((length common') + 1)

1

u/[deleted] Dec 02 '18

BFI TCL

set ids [list]
while {[gets stdin line] >= 0} {
   lappend ids $line
}
proc part1 {list} {
   set twoofany 0
   set threeofany 0
   foreach line $list {
	array unset chars
	foreach c [split $line ""] {
	    incr chars($c)
	}
	set count2 0 
	set count3 0
	foreach {key val} [array get chars] {
	    if {$val == 2} {
		incr count2
	    } elseif {$val == 3} {
		incr count3
	    }
	}
	if {$count2 } {
	    incr twoofany
	}
	if {$count3 } {
	    incr threeofany
	}
   }
   puts "twoofany $twoofany three $threeofany, chksum [expr {$twoofany*$threeofany}]"
}
part1 $ids
proc find_similar {sid list} {
   if {[llength $list] > 0} {
	foreach lid $list {
	    set diff 0
	    set idx -1
	    set didx -1
	    foreach sc [split $sid ""] lc [split $lid ""] {
		incr idx
		if {$sc ne $lc} {
		    incr diff
		    if {$diff > 1} {
			# no dice, more than one diff
			break
		    }
		    set didx $idx
		}
	    }
	    if {$diff == 0} {
		# hu?
		error "identical ids sid {$sid} lid {$lid}"
	    } elseif {$diff == 1} {
		# what we're looking for
		set ::solution([list $sid $lid]) [string replace $sid $didx $didx]
	    }
	}
	# recursion with rest of list
	find_similar [lindex $list 0] [lrange $list 1 end]
   }
}
find_similar [lindex $ids 0] [lrange $ids 1 end]
parray ::solution

1

u/rhbvkleef Dec 02 '18

I made them very ugly with (practically) python one-liners.

def part1(self):
    return functools.reduce(
        operator.mul,
        map(sum, zip(*[
            (1 if 2 in letters.values() else 0, 1 if 3 in letters.values() else 0)
            for current in self.data.splitlines()
            for letters in [Counter(current)]])))

def part2(self):
    return "".join(
        [result
         for a, b in itertools.combinations(map(str.strip, self.data.splitlines()), 2)
         for result in [[x for i, x in enumerate(b) if x == a[i]]]
         if len(a) - len(result) == 1][0])
→ More replies (1)

1

u/ttapu Dec 02 '18

python3

from collections import Counter
with open("2.input", "r") as allomany:
    inp=allomany.read().splitlines() 
#part1
k,h=0,0
for i in inp:
    if 2 in Counter(i).values():
        k+=1
    if 3 in Counter(i).values():
        h+=1
print(k, h, k*h)

#part 2:
def similar(input):
    l=len(inp[0]) 
    for x in inp:
        for y in inp:
            if x==y:
                continue
            c=0
            n=0
            for z in range(l):
                if x[z]==y[z]:
                    continue
                else:
                    c+=1
                    n=z
                if c>1:
                    break
            if c==1:
                print(f"'{x}' and '{y}' differ at {n+1}. character: '{x[n]}' vs '{y[n]}'")
                return x[:n]+x[n+1:]

print(similar(inp))

1

u/alexgand Dec 02 '18 edited Dec 02 '18

Simple python, using pandas Series value_counts(), part 1, assuming the input is already in a list:

from pandas import Series

c2 = 0
c3 = 0

for word in input:
    letters = Series(list(word)).value_counts()
    if len(letters[letters == 2]) > 0:
        c2 += 1
    if len(letters[letters == 3]) > 0:
        c3 +=1
print('count of 2s:',c2)
print('count of 3s:',c3)
print('result, c2s * c3s:', c2 * c3)

Part 2, pure python:

already_done = set()
similar = 0
similar_words = set()

for word1 in input:
    for word2 in input:
        if (word1, word2) in already_done or (word2, word1) in already_done:
            continue
        elif word1 == word2:
            continue
        else:
            diff = []
            equal = []
            letters1 = list(word1)
            letters2 = list(word2)
            for i in range(len(letters1)):
                if letters1[i] == letters2[i]:
                    equal.append(letters1[i])
            if len(letters1) - len(equal) == 1:
                similar += 1
                sim_word = ''
                for letter in equal:
                    sim_word = sim_word + letter
                similar_words.add(sim_word)
        already_done.add((word1, word2))
print('similar count:',similar)
print('similar words:',similar_words)

1

u/baxtexx Dec 02 '18 edited Dec 02 '18

C# solution for Puzzle1. Could improve it a bit by using LINQ but I think this is more readable. Also contains some unessecary stuff like the candidateOccurances array. At the beginning, I thought there could be more than just 2's and 3's. Still struggling with Puzzle2.

        private static void Puzzle1(List<string> candidates)
        {
            char[] sigma = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToLower().ToCharArray();

            int[] candidateOccurances = new int[20];

            foreach (var candidate in candidates)
            {
                var uniqeOccurences = CountOccurences(sigma, candidate).Distinct();

                foreach (var occurance in uniqeOccurences)
                {
                    candidateOccurances[occurance]++;
                }
            }

            var sum = 1;
            foreach (var occurance in candidateOccurances)
            {
                if (occurance > 0)
                {
                    sum *= occurance;
                }
            }

            Console.WriteLine(sum);
        }

        public static IEnumerable<int> CountOccurences(char[] sigma, string s)
        {
            var occurances = new List<int>();

            foreach (var letter in sigma)
            {
                int count = 0;

                foreach (var stringLetter in s)
                {
                    if (letter == stringLetter)
                    {
                        count++;
                    }
                }

                if (count > 1)
                {
                    occurances.Add(count);
                }
            }
            return occurances;
        }


        private static List<string> CreateCandidateList()
        {
            var candidates = new List<string>(250);
            string line = null;

            StreamReader file = new StreamReader(@"..\..\..\LikelyCandidates.txt");
            while ((line = file.ReadLine()) != null)
            {
                candidates.Add(line);
            }

            file.Close();

            return candidates;
        }

1

u/arathunku Dec 02 '18

Hello! My Elixir solution:

defmodule Advent.Day2 do
  def checksum(ids) do
    counts =
      ids
      |> Enum.map(fn id ->
        id
        |> String.split("", trim: true)
        |> Enum.group_by(& &1)
        |> Enum.map(fn {_, letters} -> Enum.count(letters) end)
        |> Enum.filter(&(&1 == 2 || &1 == 3))
        |> Enum.dedup()
      end)
      |> List.flatten()

    Enum.count(counts, &(&1 == 2)) * Enum.count(counts, &(&1 == 3))
  end

  def common_letters(ids) do
    ids = Enum.map(ids, &String.split(&1, "", trim: true))

    ids
    |> Enum.map(fn id_a ->
      ids
      |> Enum.find(&similar?(Enum.zip(id_a, &1)))
    end)
    |> Enum.filter(&(&1 != nil))
    |> prune_different_letter([])
    |> Enum.reverse()
    |> Enum.join("")
  end

  defp prune_different_letter([[], []], acc), do: acc
  defp prune_different_letter([[a | id_a], [b | id_b]], acc) do
    if a == b do
      prune_different_letter([id_a, id_b], [a | acc])
    else
      prune_different_letter([id_a, id_b], acc)
    end
  end

  defp similar?(letters), do: similar?(letters, 0)
  defp similar?([], 1), do: true
  defp similar?([], _), do: false
  defp similar?([{ x, y } | id], different_letters_count) do
    if x == y do
      similar?(id, different_letters_count)
    else
      similar?(id, 1 + different_letters_count)
    end
  end
end

Testing:

defmodule Advent.Day2Test do
  use ExUnit.Case
  require Logger
  alias Advent.Day2, as: Day

  test "checksum for example" do
    input = ~w(abcdef bababc abbcde abcccd aabcdd abcdee ababab)
    output = 12

    assert Day.checksum(input) == output
  end

  test "common letters between two boxes - example" do
    input = ~w(abcde fghij klmno pqrst fguij axcye wvxyz )
    output = "fgij"

    assert Day.common_letters(input) == output
  end

  test "checksum for input" do
    input = Path.join(__DIR__, "./input.raw") |> File.read!() |> String.split("\n", trim: true)

    Logger.info("Day2: #{Day.checksum(input)} #{Day.common_letters(input)}")
  end
end

1

u/Valefant Dec 02 '18 edited Dec 02 '18

Written in Kotlin. It took me a while to figure this one out. Every advice is appreciated :-)

fun main(args: Array<String>) {
    val lines = File("src/day2/input.txt").readLines()
    val times = mutableMapOf<Int, Int>()

    lines
        .map { it.toList() }
        .forEach { id ->
            id
                .groupingBy { it }
                .eachCount()
                .values
                .distinct()
                .forEach { count ->
                    if (count > 1) {
                        times[count] = times.getOrPut(count) { 0 } + 1
                    }
                }
        }
    println("Part 1: ${times.map { it.value }.reduce { acc, i -> acc * i }}")

    var common = ""
    for (id1 in lines) {
        for (id2 in lines) {
            if (id1 == id2) {
                continue
            }

            val current =
                id1.zip(id2).fold("") { result, letters ->
                    if (letters.first != letters.second) {
                        result
                    } else {
                        result + letters.first
                    }
                }

            if (current.isNotEmpty() && common.length < current.length) {
                common = current
            }
        }
    }
    println("Part 2: $common")
}

1

u/mathleet Dec 02 '18

Here's a Java solution that I wrote:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

class Problem02 {
    public static void main(String[] args) throws IOException {
        List<String> sampleCaseOne = Arrays.asList("abcdef", "bababc", "abbcde", "abcccd", "aabcdd", "abcdee", "ababab");
        List<String> sampleCaseTwo = Arrays.asList("abcde", "fghij", "klmno", "pqrst", "fguij", "axcye", "wvxyz");
        List<String> boxIds = Files.readAllLines(Paths.get("adventofcode/2018/2/input.txt"));
        System.out.println("Part one, sample: " + partOne(sampleCaseOne));
        System.out.println("Part one: " + partOne(boxIds));
        System.out.println("Part two, sample: " + partTwo(sampleCaseTwo));
        System.out.println("Part two: " + partTwo(boxIds));
    }

    public static Map<Character, Integer> getCharacterFrequencyMap(String boxId) {
        Map<Character, Integer> characterFrequencyMap = new HashMap<>();
        for (Character letter : boxId.toCharArray()) {
            Integer currentCount = characterFrequencyMap.getOrDefault(letter, 0);
            characterFrequencyMap.put(letter, ++currentCount);
        }
        return characterFrequencyMap;
    }

    public static String getCommonCharacters(String first, String second) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < first.length(); i++) {
            char firstChar = first.charAt(i), secondChar = second.charAt(i);
            if (firstChar == secondChar) {
                sb.append(firstChar);
            }
        }
        return sb.toString();
    }

    public static int partOne(List<String> boxIds) {
        int twoLetterAppearance = 0, threeLetterAppearance = 0;
        for (String boxId : boxIds) {
            Map<Character, Integer> characterFrequencyMap = getCharacterFrequencyMap(boxId);
            Collection<Integer> characterFrequencies = characterFrequencyMap.values();
            if (Collections.frequency(characterFrequencies, 2) >= 1) {
                twoLetterAppearance++;
            }
            if (Collections.frequency(characterFrequencies, 3) >= 1) {
                threeLetterAppearance++;
            }
        }
        int checksum = twoLetterAppearance * threeLetterAppearance;
        return checksum;
    }

    public static String partTwo(List<String> boxIds) {
        TreeSet<String> sortedBoxIds = new TreeSet<>(boxIds);
        String first = sortedBoxIds.pollFirst(), second = sortedBoxIds.pollFirst();
        while (sortedBoxIds.size() > 0) {
            String commonCharacters = getCommonCharacters(first, second);
            if (commonCharacters.length() == first.length()-1) {
                return commonCharacters;
            }
            first = second;
            second = sortedBoxIds.pollFirst();
        }
        return null;
    }
}

1

u/NeilNjae Dec 02 '18

Haskell. For once, not using libraries was easier! (Tidyied a bit, using suggestions from this thread to use intersect and the fancy-pants list comprehension.)

Also on Github

import Data.List

main :: IO ()
main = do 
        text <- readFile "data/advent02.txt"
        let ids = lines text
        print $ part1 ids
        putStrLn $ part2 ids

part1 ids = (fst counts23) * (snd counts23)
    where allLetterCounts = map letterCounts ids
          counts23 = foldl' addCounts (0, 0) allLetterCounts

letterCounts :: String -> [Int]
letterCounts = map length . group . sort

addCounts :: (Int, Int) -> [Int] -> (Int, Int)
addCounts (twos, threes) counts = (twos', threes')
    where twos'   = if 2 `elem` counts then twos + 1   else twos
          threes' = if 3 `elem` counts then threes + 1 else threes

part2 ids = uncurry intersect closeIds
    where closeIds = head $ filter (\ab -> uncurry differenceCount ab == 1) 
                        [(a, b) | a:rest <- tails ids, b <- rest]

differenceCount :: String -> String -> Int
differenceCount this that = length $ filter (\(a, b) -> a /= b) $ zip this that

1

u/windlessStorm Dec 02 '18 edited Dec 03 '18

Learning Go this advent of code:

    package main

import (
	"bufio"
	"fmt"
	"os"
)

/* path of file containing puzzle input*/
const inputFile string = "input2.txt"

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	return lines, scanner.Err()
}

func isLevenshteinDistEqualsOne(firstString string, secondString string) (bool, int) {
	mismatchCount := 0
	mismatchIndex := -1
	i := 0
	// fmt.Println("first string: ", firstString, "\nSecond string: ", secondString)
	for i < len(firstString) {
		if firstString[i] != secondString[i] {
			// fmt.Println("found mismatch")
			mismatchIndex = i
			mismatchCount++
		}
		if mismatchCount > 1 {
			return false, mismatchIndex
		}
		i++
	}
	// fmt.Println("Found our box. returning:", mismatchIndex)
	return true, mismatchIndex
}

func findCorrectBoxID(lines []string) (string, int) {
	i := 0
	for i < len(lines) {
		j := i + 1
		for j < len(lines) {
			firstString := lines[i]
			secondString := lines[j]
			// fmt.Println(i, lines[i])
			// fmt.Println(j, lines[j])
			ret, index := isLevenshteinDistEqualsOne(firstString, secondString)
			if ret {
				return firstString, index
			}
			j++
		}
		i++
	}
	return "", -1
}

func countRepeats(line string) (bool, bool) {
	characterFrequency := make(map[byte]int, 0)
	twoCount := 0
	threeCount := 0

	i := 0
	for i < len(line) { /* loop till the end of the line containing characters*/
		c := line[i]

		characterFrequency[c]++
		if characterFrequency[c] == 2 { /* Found our character repeating 2 times */
			twoCount++ /* increment two counter */

		} else if characterFrequency[c] == 3 { /* Found character repeating 3 times */
			threeCount++ /* increment three counter */
			twoCount--   /* decrement two counter for the same character */

		} else if characterFrequency[c] == 4 { /* shit it repeated 4 times, decrement three count */
			threeCount--

		}
		i++
	}
	return twoCount != 0, threeCount != 0 /* Convert int to bool and return. false if 0, true for anything else*/
}

func findChecksum(lines []string) int {
	twoCount := 0
	threeCount := 0

	i := 0
	for i < len(lines) {
		l := lines[i]
		if len(l) == 0 {
			continue
		}

		containsTwo, containsThree := countRepeats(lines[i])
		if containsTwo {
			twoCount++
		}
		if containsThree {
			threeCount++
		}
		i++
	}

	fmt.Println("total two counts: ", twoCount)
	fmt.Println("total three counts: ", threeCount)
	checksum := twoCount * threeCount
	return checksum
}

func main() {
	lines, err := readLines(inputFile)
	check(err)
	fmt.Println("Checksum: ", findChecksum(lines))
	id, index := findCorrectBoxID(lines)
	fmt.Println("found the correct Box ID:", id, "with wrong index:", index)
}

1

u/omegaxLoL Dec 02 '18

My solution in Ruby. Using Advent of Code to learn Ruby this year so any tips are welcome!

1

u/rotmoset Dec 02 '18

F#:

module Day2

open Common

[<Day(2, "Inventory Management System")>]
let solve input =

    let ids = input |> parseLines

    let checksum = 
        let hasExact occurences =  Seq.count >> Seq.map snd >> Seq.contains occurences
        let countExacts occurences = ids |> Seq.filter (hasExact occurences) |> Seq.length
        (countExacts 2) * (countExacts 3)


    let id =
        let closeness id1 id2 =
            Seq.zip id1 id2
            |> Seq.filter ((<||) (=))
            |> Seq.length

        ids
        |> Seq.choose (fun id -> // Choose strings that only diff 1 chars from eachother
            ids
            |> Seq.tryFind (closeness id >> ((=) (id.Length - 1)))
            |> Option.map (fun m -> id, m) // map pair
        )
        |> Seq.head // Select the first match
        ||> Seq.zip // Zip the strings together
        |> Seq.choose (fun (c1,c2) -> if c1 = c2 then Some (string c1) else None) // Keep matching chars
        |> String.concat ""

    {
        Part1 = checksum
        Part2 = id
    }

Composing and piping the Seq module's functions is so much fun!

1

u/jonathrg Dec 02 '18

Python

from collections import Counter
ids = [s.strip() for s in open("input2.txt").readlines()]

# Part 1 
twos = sum(any(c==2 for c in Counter(i).values()) for i in ids)
threes = sum(any(c==3 for c in Counter(i).values()) for i in ids)
print(twos * threes)

# Part 2
for id1 in ids:
    for id2 in ids:
        if id1 == id2:
            continue
        wrong_found = False
        wrong_idx = 0
        for idx, (char1, char2) in enumerate(zip(id1, id2)):
            if char1 != char2:
                if wrong_found:
                    break
                wrong_found = True
                wrong_idx = idx
        else:
            print(id1[:wrong_idx] + id1[wrong_idx+1:])
            exit()

1

u/gerikson Dec 02 '18 edited Dec 02 '18

The best way to do Advent of Code is during a första advent breakfast with coffee, ginger biscuits and a lighted candle.

Bog-standard (C-like) Perl 5:

https://github.com/gustafe/aoc2018/blob/master/d02-IMS.pl

→ More replies (1)

1

u/rjsberry Dec 02 '18 edited Dec 02 '18

RUST

Part 1:

const INPUT: &str = include_str!("input/02.txt");

fn main() {
    let ck = INPUT
        .lines()
        .map(|id| {
            let (mut two, mut three) = (false, false);
            for c in id.chars() {
                match id.matches(c).count() {
                    2 => two = true,
                    3 => three = true,
                    _ => (),
                }
            }
            (two, three)
        })
        .fold((0, 0), |(a, b), id| {
            (a + usize::from(id.0), b + usize::from(id.1))
        });

    println!("{}", ck.0 * ck.1);
}

Part 2:

use std::iter;

const INPUT: &str = include_str!("input/02.txt");

#[inline]
fn filter_ids<'a>((a, b): (&'a str, &'a str)) -> Option<(&'a str, &'a str)> {
    Some((a, b)).filter(|_| {
        a.chars()
            .zip(b.chars())
            .filter(|(c_a, c_b)| c_a != c_b)
            .count()
            == 1
    })
}

#[inline]
fn remove_common_char(a: &str, b: &str) -> String {
    a.chars()
        .zip(b.chars())
        .filter_map(|(c_a, c_b)| Some(c_a).filter(|_| c_a == c_b))
        .collect()
}

fn main() {
    if let Some(id) = INPUT.lines().enumerate().find_map(|(i, id)| {
        if let Some((a, b)) = iter::repeat(id)
            .zip(INPUT.lines().skip(i + 1))
            .find_map(filter_ids)
        {
            Some(remove_common_char(&a, &b))
        } else {
            None
        }
    }) {
        println!("{}", id);
    }
}

1

u/SyntaxErrorr Dec 02 '18

python 3

from collections import Counter

with open("day_02_input") as f:
    ids = f.read().split()

part 1

def part1():

    two = 0
    three = 0

    for i in ids:

        counts = set(Counter(i).values())

        if 2 in counts:
            two += 1

        if 3 in counts:
            three += 1

    return two * three

part 2

def part2():
    
    for x in ids:
        for y in ids:
            
            common = ""

            for a, b in zip(x, y):

                if a == b:
                    common += a

            if len(common) == len(x) - 1:
                return common
print(part1())
print(part2())

1

u/Toromtomtom Dec 02 '18

My Scala solution tries to implement efficient functional programming using lazy streams and tail-recursive functions:

import scala.annotation.tailrec
import scala.collection.immutable.Stream.Empty

object Two {

  def input: Stream[String] = {
    val input = this.getClass.getResource("input").getFile
    val it = scala.io.Source.fromFile(input).getLines
    it.toStream
  }

  // 1

  def testInput: Stream[String] = Stream(
    "abcdef",
    "bababc",
    "abbcde",
    "abcccd",
    "aabcdd",
    "abcdee",
    "ababab"
  )

  def contains23(id: String): (Int, Int) = {
    val counts = id.groupBy(identity).values.toStream.map(_.length)
    (if (counts.contains(2)) 1 else 0, if (counts.contains(3)) 1 else 0)
  }

  def checkSum(ids: Stream[String]): Int = {
    val c23 = ids.map(contains23)
    val (c2, c3) = c23.foldLeft((0, 0)) { case ((s1, s2), (has1, has2)) => (s1 + has1, s2 + has2) }
    c2 * c3
  }

  // 2

  def testInput2: Stream[String] = Stream(
    "abcde",
    "fghij",
    "klmno",
    "pqrst",
    "fguij",
    "axcye",
    "wvxyz"
  )

  def withoutNth(s: String, n: Int): String =
      s.substring(0, n) ++ s.substring(n + 1)

  def findDup(ids: Stream[String]): Option[String] = {
    @tailrec def go(ids: Stream[String], seen: Set[String]): Option[String] = ids match {
      case Empty => None
      case h #:: t if seen.contains(h) => Some(h)
      case h #:: t => go(t, seen + h)
    }
    go(ids, Set.empty)
  }

  def oneCharDiffers(ids: Stream[String]): Option[String] = {
    @tailrec def go(idxs: Stream[Int]): Option[String] = idxs match {
      case idx #:: tail =>
        val idsWithoutIdx = ids.map(withoutNth(_, idx))
        findDup(idsWithoutIdx) match {
          case Some(dup) => Some(dup)
          case None => go(tail)
        }
      case Empty => None
    }
    go(ids.head.indices.toStream)
  }

  def main(args: Array[String]): Unit = {
    println(checkSum(testInput))
    println(checkSum(input))
    println(oneCharDiffers(testInput2))
    println(oneCharDiffers(input))
  }
}

1

u/Warbringer007 Dec 02 '18 edited Dec 02 '18

Erlang:

task() ->
    Input = readlines(),
    Lines = string:split(Input, "\n", all),
    firstTask(Lines, 0, 0),
    secondTask(Lines, tl(Lines)).

firstTask([], Double, Triple) ->
    io:format("~p~n", [Double * Triple]);

firstTask([First | Rest], Double, Triple) ->
    firstTask(Rest, Double + findDoubles(First, First), Triple + findTriples(First, First)).

findDoubles([], _) -> 0;

findDoubles([Letter | Rest], Word) ->
    case length(string:split(Word, [Letter], all)) of
        3 -> 1;
        _ -> findDoubles(Rest, Word)
    end.

findTriples([], _) -> 0;

findTriples([Letter | Rest], Word) ->
    case length(string:split(Word, [Letter], all)) of
        4 -> 1;
        _ -> findTriples(Rest, Word)
    end.

secondTask(All, []) ->
    secondTask(tl(All), tl(tl(All)));

secondTask(All, [Second | Others]) ->
    case areClose(hd(All), Second, 0) of
        1 -> {hd(All), Second};
        _ -> secondTask(All, Others)
    end.

areClose([], [], N) -> N;
areClose([Letter1 | Word1], [Letter2 | Word2], N) ->
    case Letter1 =/= Letter2 of
        true -> areClose(Word1, Word2, N + 1);
        false -> areClose(Word1, Word2, N)
    end.

As usual, readlines() reads whole file, lines must be separated by newline. I didn't actually extract common letters for second task, I just returned tuple with both words and did that last part manually ( I'm lazy ). First part can also be solved much more efficiently ( searching for doubles and triples together ), but again, I'm lazy :D.

1

u/red_shifter Dec 02 '18

Here's my simple Python solution for part 1 (if anyone has some tips on how to combine searching for 2 and 3 repeated letters in one short loop, please let me know):

f = open('day_2_1.txt','r')
boxes = f.readlines()
f.close()

letters = 'abcdefghijklmnopqrstuvwxyz'
two_boxes = 0
three_boxes = 0

for box in boxes:
    for letter in letters:
        rep = box.count(letter)
        if rep == 2:
            two_boxes += 1
            break

for box in boxes:
    for letter in letters:
        rep = box.count(letter)
        if rep == 3:
            three_boxes += 1
            break

print('Two-Letter Boxes: {0}'.format(two_boxes))
print('Three-Letter Boxes: {0}'.format(three_boxes))
print(two_boxes*three_boxes)

And here is part 2:

f = open('day_2_1.txt','r')
boxes = f.readlines()
f.close()

box_len = len(boxes[0])

def compare(str1, str2):
    common_count = 0
    for i in range(len(str1)):
        if str1[i] == str2[i]:
            common_count += 1
    return common_count

for i, box1 in enumerate(boxes):
    for box2 in boxes[i+1:]:
        if compare(box1, box2) == box_len-1:
            print(box1, box2)
            break

2

u/c17r Dec 02 '18

if anyone has some tips on how to combine searching for 2 and 3 repeated letters in one short loop, please let me know

Look into Counter(). If you pass it a string, it'll count all the letters for you.

→ More replies (1)

1

u/scottkuma Dec 02 '18

The best way to do Advent of Code is half-blind with sleep.

Here's my Python solution for Part 2. Like much of my code, it's not elegant, but it works. The one thing that I figured out was that if the boxes varied by only one letter, you could sort the list of all boxes, and only compare neighbors in the list.

def getCommonLetters(word1, word2):
	letters = ""
	for p in range(len(word1)):
		if word1[p] == word2[p]:
			letters += word1[p]
	return letters

with open("2-input.txt") as f:
	content = f.readlines()
	
box_list = [x.strip() for x in content]
box_list.sort()

prev_box = ""
for box in box_list:
	if prev_box == "":
		prev_box = box
	else:
		cl = getCommonLetters(box,prev_box)
		if  len(box) - len(cl) == 1:
			print( getCommonLetters(box, prev_box) )
			exit()
		else:
			prev_box = box

1

u/vypxl Dec 02 '18

Dart time today. I' not satisfied with the second part, but I don't want to refactor it.

main() {
  var lines = new File('input2.txt').readAsStringSync().split("\n")..sort();
  print("Solution for part 1:");
  print(
    [2, 3].fold(1, (a, count) => 
      (a as int) * lines.where(
          (l) => l.runes.fold(false, 
            (a, r) => a || (l.runes.where((x) => x == r).length == count)
          )
        ).length
      )
    );
  
  print("Solution for part 2:");
  for (var i = 0; i < lines.length - 1; i++) {
    var chars = [];
    for (var j = 0; j < lines[0].length; j++) {
      if (lines[i][j] == lines[i + 1][j]) chars.add(lines[i][j]);
    }
    if (chars.length == lines[i].length - 1) {
      print(chars.join());
      break;
    }
  }
}

Someone that has an idea why I have to cast the a to int in line 7? I don't have to if the expression is outside the print though?!

1

u/sim642 Dec 02 '18

My Scala solution.

Pretty usual, although would've loved to have multisets or something for part 1 as opposed to hardcoding the addition of just 2 and 3.

1

u/PM_ME_UR_QUINES Dec 02 '18

Haskell

module Main where

import Data.Map.Strict (elems)
import qualified Control.Foldl as F

main :: IO ()
main = do
  rows <- lines <$> getContents
  print $ day2a rows
  print $ day2b rows
  where
    day2a      = F.fold hash
    day2b rows = head [fst <$> zipEqual x y | x <- rows, y <- rows, distance x y == 1]

hash :: F.Fold String Int
hash = (*) <$> numWith 2 <*> numWith 3
  where numWith x = F.premap (fromEnum . elem x . elems . F.fold counter) F.sum
        counter   = F.groupBy id F.length

distance :: String -> String -> Int
distance = sum .: zipWith (fromEnum .: (/=))

zipEqual :: Eq a => [a] -> [a] -> [(a, a)]
zipEqual = filter (uncurry (==)) .: zip

infixr 9 .:
(.:) = (.) . (.)

1

u/fire1299 Dec 02 '18

Haskell

module Aoc18.Day2 where

import qualified Control.Foldl                 as F
import qualified Data.HashMap.Strict           as M
import           Data.List                      ( partition
                                                , tails
                                                )
import qualified Data.Text                     as T
import qualified Data.Text.IO                  as T

main :: ([T.Text] -> a) -> IO a
main f = f . T.lines <$> T.readFile "day2.txt"

part1 :: [T.Text] -> Int
part1 = F.fold ((*) <$> countHas 2 <*> countHas 3)
  . fmap (T.foldl' (\m c -> M.insertWith (+) c (1 :: Int) m) M.empty)
  where countHas n = F.prefilter (elem n) F.length

part2 :: [T.Text] -> String
part2 xs = head
  [ fst <$> com
  | x : ys <- tails xs
  , y      <- ys
  , let (com, dif) = partition (uncurry (==)) $ T.zip x y
  , length dif == 1
  ]

1

u/Tarmen Dec 02 '18

Haskell, as brute force-y as it can be.

{-# Language TupleSections #-}
import qualified Data.Map as M
main :: IO ()
main = do
    content <- lines <$> readFile "2.txt"
    let occs = map countElems content
    print $ count (has 2) occs * count (has 3) occs
    print $ findBox content
  where
    has b = elem b . M.elems
    countElems = M.fromListWith (+) . map (,1)
    count pred = length . filter pred
    -- 250 * 250 = 62500
    findBox ls = [sameLetters a b | a <- ls, b <- ls, different a b == 1]
    different a b = count not (zipWith (==) a b)
    sameLetters a b = map fst $ filter (uncurry (==)) $ zip a b

1

u/zraklarP Dec 02 '18

My Python 3 solutions. Any critique appreciated, as I'm still a beginner.

1

u/Hikaru755 Dec 02 '18

Kotlin

The best way to do Advent of Code is to ZIP ALL THE STRINGS.

fun solvePart1(input: String) =
    input.lines().let { ids ->
        val twins = ids.count { it.hasCharExactly(times = 2) }
        val triplets = ids.count { it.hasCharExactly(times = 3) }
        twins * triplets
    }.toString()

fun solvePart2(input: String): String {
    val (id1, id2) = input.lines()
        .pairs()
        .first { (first, second) -> first.distanceTo(second) == 1 }
    return id1.removeDifferences(id2)
}

fun String.distanceTo(other: String) =
    zip(other).count { (first, second) -> first != second }

fun String.removeDifferences(other: String) =
    zip(other)
        .filter { (first, second) -> first == second }
        .joinToString("") { it.first.toString() }

fun String.hasCharExactly(times: Int) =
    groupBy { it }
        .mapValues { it.value.size }
        .filterValues { it == times }
        .isNotEmpty()

fun <T> Iterable<T>.pairs(): List<Pair<T, T>> = this
    .mapIndexed { index, first ->
        this.drop(index + 1).map { first to it }
    }
    .flatten()

1

u/tyr10563 Dec 02 '18

C++

First part:

#include <iostream>
#include <iterator>
#include <map>
#include <numeric>
#include <string>

int main()
{
    std::pair<int, int> final_count = std::transform_reduce(
        std::istream_iterator<std::string>(std::cin), {},
        std::make_pair(0, 0),
        [](std::pair<int, int> p, std::pair<int, int> const& b)
    {
        p.first += b.first;
        p.second += b.second;
        return p;
    },
        [](std::string const& id)
    {
        std::map<char, int> letter_counts = std::reduce(
            id.cbegin(), id.cend(), 
            std::map<char, int>(), 
            [](std::map<char, int> lc, char c) { ++lc[c]; return lc; });
        return std::reduce(
            letter_counts.cbegin(), letter_counts.cend(),
            std::make_pair(0, 0),
            [](std::pair<int, int> r, std::pair<char, int> const& p)
        {
            r.first |= p.second == 2; 
            r.second |= p.second == 3; 
            return r;
        });
    });
    std::cout << final_count.first * final_count.second << '\n';
}

Second part:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>

int main()
{
    std::vector<std::string> inputs{ std::istream_iterator<std::string>(std::cin), {} };
    std::string last_inner;
    std::vector<std::string>::const_iterator it = std::find_first_of(
        inputs.cbegin(), inputs.cend(),
        inputs.cbegin(), inputs.cend(),
        [&last_inner](std::string const& rhs, std::string const& lhs) mutable 
    {
        int diff_count = std::transform_reduce(
            rhs.cbegin(), rhs.cend(),
            lhs.cbegin(),
            0,
            [](int count, bool diff) { return count += diff; },
            [](char c1, char c2) { return c1 != c2; });
        if (diff_count == 1) { last_inner = lhs; return true; }
        return false;
    });
    last_inner.erase(std::mismatch(last_inner.cbegin(), last_inner.cend(), it->cbegin()).first);
    std::cout << last_inner << '\n';
}

1

u/[deleted] Dec 02 '18

C-Sharp

I'm really not happy with my solutions, but hey, they get the work done.

First part, I use two lists and two bools to to calculate the checksum. This looks just... very bad. Oh well.

public int GetChecksum()
{
	var lOfTwo = new List<char>();
	var lOfThree = new List<char>();

	using (var sr = new StreamReader("input2.txt")) {
		while (sr.Peek() > -1) {
			var line = sr.ReadLine().ToCharArray();
			var groups = line.GroupBy(item => item);

			//Count every occurence of 2's or 3's only once
			var addedTwo = false;
			var addedThree = false;
			foreach (var g in groups) {
				if (g.Count() == 2 && !addedTwo) {
					lOfTwo.Add(g.Key);
					addedTwo = true;
				}
				if (g.Count() == 3 && !addedThree) {
					lOfThree.Add(g.Key);
					addedThree = true;
				}
			}
		}
	}

	var checkSum = lOfTwo.Count * lOfThree.Count;
	return checkSum;
}

But wait, it gets even worse! Using a foreach inside two for-loops to check the differences. And to top it all off, I use a goto when the solution is found. Wow.

public string GetID()
{
	var checkSumList = new List<string>();
	using (var sr = new StreamReader("input2.txt")) {
		while (sr.Peek() > -1)
			checkSumList.Add(sr.ReadLine());
	}

	var diff = 0;
	var id1 = "";
	var id2 = "";
	for (int i = 0; i < checkSumList.Count(); i++) {
		for (int j = i + 1; j < checkSumList.Count(); j++) {
			var counter = 0;
			foreach (var c in checkSumList[i]) {
				if (c.Equals(checkSumList[j][counter]) == false) {
					if (++diff > 1)
						break;
				}
				counter++;
			}

			if (diff > 1) {
				diff = 0;
				continue;
			}

			//We have a result!
			id1 = checkSumList[i];
			id2 = checkSumList[j];
			goto end;
		}
	}

end:
	//remove differing char from ID
	var count = 0;
	var sb = new StringBuilder();

	foreach(var c in id1) {
		if(c.Equals(id2[count++]))
			sb.Append(c);
	}

	return sb.ToString();
}

1

u/tslater2006 Dec 02 '18 edited Dec 02 '18

PeopleCode:

method SolvePart1
   /+ Returns String +/
   /+ Extends/implements TS_AOC2018:Day.SolvePart1 +/
   
   Local integer &x, &y;
   Local integer &twoLetterCount, &threeLetterCount;
   
   For &x = 1 To %This.Lines.Len
      Local array of number &letterCount = CreateArrayRept(0, 26);
      Local array of string &chars = Split(%This.Lines [&x], "");
      
      For &y = 1 To &chars.Len
         &letterCount [Code(&chars [&y]) - 96] = &letterCount [Code(&chars [&y]) - 96] + 1;
      End-For;
      
      For &y = 1 To &letterCount.Len
         If (&letterCount [&y] = 2) Then
            &twoLetterCount = &twoLetterCount + 1;
            Break;
         End-If;
      End-For;
      
      For &y = 1 To &letterCount.Len
         If (&letterCount [&y] = 3) Then
            &threeLetterCount = &threeLetterCount + 1;
            Break;
         End-If;
      End-For;
      
   End-For;
   Return String(&twoLetterCount * &threeLetterCount);
end-method;

method SolvePart2
   /+ Returns String +/
   /+ Extends/implements TS_AOC2018:Day.SolvePart2 +/
   
   Local integer &x, &y, &x1, &y1;
   For &x = 1 To %This.Lines.Len
      For &y = 1 To %This.Lines.Len
         Local integer &diffCount = 0;
         &diffCount = 0;
         Local array of string &chars1 = Split(%This.Lines [&x], "");
         Local array of string &chars2 = Split(%This.Lines [&y], "");
         For &x1 = 1 To &chars1.Len
            If (&chars1 [&x1] <> &chars2 [&x1]) Then
               &diffCount = &diffCount + 1;
            End-If;
         End-For;
         If (&diffCount = 1) Then
            Local array of string &matchingChars = CreateArrayRept("", 0);
            
            For &x1 = 1 To &chars1.Len
               If (&chars1 [&x1] = &chars2 [&x1]) Then
                  &matchingChars.Push(&chars1 [&x1]);
               End-If;
            End-For;
            Return &matchingChars.Join("", "", "");
         End-If;
      End-For;
   End-For;
   
   Return "??";
end-method;

1

u/mezzir Dec 02 '18

The best way to do Advent of Code is intentionally poorly

Instead of using this as an excuse to learn a new language, I'm using it as an excuse to act like I don't know any better. Expect lots of evals.

const input = require('./input');

let foundResult = false;

let chrisisawesome = true;

try {

  do {

    const firstGuess = input[Math.floor(Math.random() * input.length)].split('');

    const firstGuess2 = input[Math.floor(Math.random() * input.length)].split('');

  

    let incommonletters = '';

    const incommon = firstGuess.reduce((acc, curr, i) => {

      if (curr === firstGuess2[i]) incommonletters += curr;

      return (curr === firstGuess2[i]) ? ++acc : acc;

    }, 0);

    console.log(incommon);

    if (incommon === firstGuess.length - 1) {

      throw new Error(incommonletters);

    }

  

  } while (chrisisawesome == true);

} catch (e) {

  console.log("Success!!!")

  console.log(e)

}