Advent of code

I took a quick look and day 4 of Advent of Code seems to be the easiest to solve so far. I think it takes just a few lines of code and can be solved very efficiently using a binary trick.

Here’s another pitfall I ran into with Swift when solved Day 3:

There’s a different behavior between

dict["a",default:[]] .append(1)

and

var value = dict["a",default:[]]
value.append(1)

The first updates the dictionary’s value for key “a”, the latter won’t.

Who would have known?

1 Like

Are you in the leaderboard? If not, send Kem you ID so that he adds you to “ours”.

And if you like more detailed statistics of our leaderboard performance, get the FF / Chrome extension “Advent of Code Charts”, see GitHub - jeroenheijmans/advent-of-code-charts: Inject charts in your private leaderboard page for Advent of Code

no, still have to learn a lot.

That’s how I recall NSDictionaries working also. I once knew why, but I don’t recall now.

Here is my solution for day 4 part 1. Anyone got a Swift solution to share?

\ We use buffer as bitmap of bits
: bitmap \ maxbits -- b
  8 n:/mod swap if
    n:1+
  then 
  b:new b:clear ;

\ transform an array of numbers into buffer with those bits set
: >bitmap  \ a -- b
  99 bitmap swap
  ( 1 b:bit! ) a:each! drop ;

\ How many bits are set inside the buffer?
: bits-set?  \ b -- n
  0 >r ( b:bit@ if 1 n:r+ then ) 0 99 loop drop r> ;
 
: app:main
  0 >r "4.txt" f:slurp
  ( ":" s:/ 1 a:_@ "|" s:/ ( " " s:/ ( "" s:= not ) a:filter ' >n a:map ) a:map
    ' >bitmap a:map a:open ' n:band b:op bits-set?
    [0,1,2,4,8,16,32,64,128,256,512] caseof n:r+ 
  ) s:eachline r> . cr ;
1 Like

No, I am not in the leaderboard. It’s a bit bad time on workdays for me here in Finland when new puzzles come available.

Swift:

Spoiler
var cards: [(Set<Int>,[Int])]!

func setupInput(input: String) {
	var cards = [(Set<Int>,[Int])]()
	for card in input.components(separatedBy: CharacterSet.newlines) {
		let winnersAndOwn = card.components(separatedBy: ":").last!.components(separatedBy: "|")
		let winners = Set (winnersAndOwn[0].components(separatedBy: " ").compactMap { Int($0) })
		let own = winnersAndOwn[1].components(separatedBy: " ").compactMap { Int($0) }
		cards.append((winners,own))
	}
	self.cards = cards
}

func part1() async throws -> Any {
	var result = 0

	for card in self.cards {
		let hits = card.0.intersection(card.1).count
		if hits > 0 {
			let score = 1 << (hits-1)
			result += score
		}
	}
	
	return result
}

func part2() async throws -> Any {
	var wonCards = [Int:Int]()
	var cardNo = 0
	for card in self.cards {
		cardNo += 1
		let hits = card.0.intersection(card.1).count
		if hits > 0 {
			let usedCards = wonCards[cardNo,default:0] + 1
			for n in 1...hits {
				wonCards[cardNo+n,default:0] += usedCards
			}
		}
	}

	let result = self.cards.count + wonCards.map{$0.value}.reduce(0,+)
	return result
}

NSDicts work like how? Fact is, they work the way I had described them in my SO question, ie. you’d get an NSMutableArray from the dict values and then can add to the array, which is the same as the array stored in the dict. Same behavior in Xojo (at least with the original API’s Dictionary and Arrays)

The 8th code is always impressively short. Not knowing the 8th syntax I wouldn’t have the slightest clue what that code does without your comments.

That’s because you and I had different experience with NSDictionaries. I learned to manipulate sub dictionaries/arrays/objects by always calling [NSObject mutableCopy] to ensure I could modify it.

When I wrote dictionary code with Swift, I experienced the same behavior. So I assumed it was the same, especially as I’d learned that ‘var object = object’ is how you can create a mutable copy of an object.

I found this online, which explains it.

Swift arrays and dictionaries are value types. When you assign one that is in a variable to another variable, you are making a copy of the original.

Interest piqued me so I fired up Postgres for the first time in ages after all this is data(!) so wanted to play with SQL to get it done. Loaded sample data into a table and off we went.

Did day 1 with no problems with 2 small functions and takes 0.516 seconds to run, sure I could optimise it and make it one function but hey-ho

SELECT
	sampleid,
	sampledata,
	public.fstnumber (sampledata),
	public.lstnumber (sampledata),
	(public.fstnumber (sampledata) * 10) + public.lstnumber (sampledata) AS "calibrationvalue"
INTO TEMP TABLE sampledataresults
FROM 
	tbl_day1sampledata;

SELECT SUM(calibrationvalue) FROM sampledataresults;	

DROP TABLE sampledataresults;
-- Sample function increments the input value by 1
CREATE OR REPLACE FUNCTION fstnumber(searchdata TEXT)
RETURNS INT AS $$

	-- Variable declarations
	DECLARE ival SMALLINT DEFAULT 0;

	BEGIN
	
		WITH firstNum AS (
			SELECT 
				s.sampledata, 
				c.intvalue,
				row_number() OVER (partition by s.sampledata order by STRPOS(LOWER(s.sampledata), c.compvalue)) as num
			FROM tbl_day1sampledata AS s CROSS JOIN tbl_comparisonterms AS c
			WHERE 
				s.sampledata = searchdata AND
				STRPOS(LOWER(s.sampledata), c.compvalue) > 0
		)
		
		SELECT intvalue INTO ival
		FROM firstNum 
		WHERE num = 1;
		
		RETURN ival;
	END;
$$ LANGUAGE plpgsql;

-- Sample function increments the input value by 1
CREATE OR REPLACE FUNCTION lstnumber(searchdata TEXT)
RETURNS INT AS $$

	-- Variable declarations
	DECLARE ival SMALLINT DEFAULT 0;

	BEGIN
	
		WITH secNum AS (
			SELECT  
				c.intvalue,
				row_number() OVER (partition by s.sampledata order by STRPOS(REVERSE(LOWER(s.sampledata)), REVERSE(c.compvalue))) as num
			FROM tbl_day1sampledata AS s CROSS JOIN tbl_comparisonterms AS c
			WHERE
				s.sampledata = searchdata AND
				STRPOS(REVERSE(LOWER(s.sampledata)), REVERSE(c.compvalue)) > 0
		)
		
		SELECT intvalue INTO ival
		FROM secNum 
		WHERE num = 1;
		
		RETURN ival;
	END;
$$ LANGUAGE plpgsql;
2 Likes

And the first part of day 2 - I’m on a roll! :slight_smile: 0.083 seconds to run

SELECT
	gameid,
	gamenr,
	 regexp_split_to_array(
		unnest(string_to_array(gamedata, ';'))
	, ',') AS gamesets
INTO TEMPORARY tmp_gamesets
FROM
	public.tbl_day2sampledata;
	
SELECT
	gameid,
	gamenr,
	regexp_replace(gamesets[1], '\D','','g') AS "gameset1value",
	LTRIM(regexp_replace(gamesets[1], '[[:digit:]]','','g')) AS "gameset1colour",
	regexp_replace(gamesets[2], '\D','','g') AS "gameset2value",
	LTRIM(regexp_replace(gamesets[2], '[[:digit:]]','','g')) AS "gameset2colour",
	regexp_replace(gamesets[3], '\D','','g') AS "gameset3value",
	LTRIM(regexp_replace(gamesets[3], '[[:digit:]]','','g')) AS "gameset3colour"
INTO TEMPORARY tmp_extractedgamesetvalues
FROM tmp_gamesets;

DELETE FROM tmp_extractedgamesetvalues 
WHERE gameid IN (
	SELECT DISTINCT(gameid)
	FROM tmp_extractedgamesetvalues
	WHERE
		CASE gameset1colour
			WHEN 'blue' THEN CAST(gameset1value AS INT) > 14
			WHEN 'red' THEN CAST(gameset1value AS INT) > 12
			WHEN 'green' THEN CAST(gameset1value AS INT) > 13
		END OR
		CASE gameset2colour
			WHEN 'blue' THEN CAST(gameset2value AS INT) > 14
			WHEN 'red' THEN CAST(gameset2value AS INT) > 12
			WHEN 'green' THEN CAST(gameset2value AS INT) > 13
		END OR
		CASE gameset3colour
			WHEN 'blue' THEN CAST(gameset3value AS INT) > 14
			WHEN 'red' THEN CAST(gameset3value AS INT) > 12
			WHEN 'green' THEN CAST(gameset3value AS INT) > 13
		END
);

SELECT SUM(DISTINCT(gameid)) AS "Total"
FROM tmp_extractedgamesetvalues;
		
DROP TABLE tmp_extractedgamesetvalues;
DROP TABLE tmp_gamesets;
4 Likes

The last one I promise, I’m not going to keep posting solutions - you’ll all get the hump - but did the 2nd half of day 2 - 0.094 execution time. Swift and whatever other languages? It’s data and you won’t beat SQL for set-based, speedy goodness!

I haven’t looked at optimising anything :grinning:

SELECT
	gameid,
	gamenr,
	 regexp_split_to_array(
		unnest(string_to_array(gamedata, ';'))
	, ',') AS gamesets
INTO TEMPORARY tmp_gamesets
FROM
	public.tbl_day2sampledata;
	
SELECT
	gameid,
	gamenr,
	CASE 
		WHEN LTRIM(regexp_replace(gamesets[1], '[[:digit:]]','','g')) = 'blue' THEN regexp_replace(gamesets[1], '\D','','g')
		WHEN LTRIM(regexp_replace(gamesets[2], '[[:digit:]]','','g')) = 'blue' THEN regexp_replace(gamesets[2], '\D','','g')
		ELSE regexp_replace(gamesets[3], '\D','','g')
	END AS "blue",
	CASE 
		WHEN LTRIM(regexp_replace(gamesets[1], '[[:digit:]]','','g')) = 'red' THEN regexp_replace(gamesets[1], '\D','','g')
		WHEN LTRIM(regexp_replace(gamesets[2], '[[:digit:]]','','g')) = 'red' THEN regexp_replace(gamesets[2], '\D','','g')
		ELSE regexp_replace(gamesets[3], '\D','','g')
	END AS "red",
		CASE 
		WHEN LTRIM(regexp_replace(gamesets[1], '[[:digit:]]','','g')) = 'green' THEN regexp_replace(gamesets[1], '\D','','g')
		WHEN LTRIM(regexp_replace(gamesets[2], '[[:digit:]]','','g')) = 'green' THEN regexp_replace(gamesets[2], '\D','','g')
		ELSE regexp_replace(gamesets[3], '\D','','g')
	END AS "green"
INTO TEMPORARY tmp_extractedgamesetvalues
FROM tmp_gamesets;

SELECT
	gameid,
	MAX(CAST(blue AS INT)) * MAX(CAST(red AS INT)) * MAX(CAST(green AS INT)) AS "total"
INTO temporary tmp_aggregatedvals
FROM tmp_extractedgamesetvalues
GROUP BY gameid;

SELECT SUM(total) AS "GrandTotal" FROM tmp_aggregatedvals;

DROP TABLE tmp_extractedgamesetvalues;
DROP TABLE tmp_gamesets;
DROP TABLE tmp_aggregatedvals;
2 Likes

Interesting… I did not think day 2 part 2 as a set based problem, just a compare and basic math.

My 8th programming language version takes about 130 ms to run but that includes startup and JIT compilation time. If I time just the code, it takes about 5 ms to run.

That’s not right, though. Either you already have put a NSMutableArray object into the dict’s values, and then retrieving the item later again will give you the same mutable object back (without the need to request a mutable copy), or you had put in an immutable NSArray, but then getting that and making it mutable won’t update the value stored in the dict.

In my experience dictionaries from API are not mutable and I couldn’t find a reliable way of testing. I also found at some point over the years, that mutable copy does not apply to leaves, unless you serialize the dictionary, and then de-serialize it as a mutable dictionary.

Bearing in mind that most of my dictionary editing was with image meta data or Info.plist files. It took me a while to find the right API to load a dictionary as mutable, leaves and all.

So I adopted that pattern and I didn’t even think to see if it was the right way to do it not.

https://forums.swift.org/t/advent-of-code-2023/68749