Compare commits

..

No commits in common. "main" and "gitea-actions" have entirely different histories.

26 changed files with 1001 additions and 1686 deletions

View File

@ -17,8 +17,6 @@ jobs:
run: go build -v ./day01/*.go
- name: Build day two
run: go build -v ./day02/*.go
- name: Run day one
run: go run -v ./day01/*.go
- name: Display Go version
run: go version
- name: List files in the repository

1
.gitignore vendored
View File

@ -22,4 +22,3 @@ go.work
# Problem data
input*.txt
inputs/

View File

@ -1,16 +0,0 @@
# Advent of Code 2023
An attempt to start programming again.
- [Day 1](./day01/trebuchet.go)
- [Day 2](./day02/lottacubes.go)
- [Day 3](./day03/engine-schema.go)
- [Day 4](./day04/scratchcards.go)
- [Day 5](./day05/seeds.go)
- [Day 6](./day06/race.go)
- [Day 7](./day07/cards.go)
- [Day 8](./day08/charpath.go)
- [Day 9](./day09/oasis.go)
This repo gets automatically mirrored to [Github](https://github.com/Doddophonique/aoc2023).

View File

@ -1,24 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

1000
day01/input Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
../inputs

View File

@ -109,7 +109,7 @@ func CombineIndexes(s string) int {
}
func main() {
file, err := os.Open("./inputs/day01_input")
file, err := os.Open("input")
check(err)
defer file.Close()

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,175 +0,0 @@
package main
import (
"fmt"
"strings"
"bufio"
"os"
"strconv"
)
func check(e error) {
if e != nil {
panic(e)
}
}
var requiredCubes = map[string]int {
"red": 12,
"green": 13,
"blue": 14,
}
type possibleGame struct {
redIsPossible bool
greenIsPossible bool
blueIsPossible bool
}
type minimumSet struct {
red int
green int
blue int
}
func splitSets (s string) ([]string, int) {
// Every set is divided by ;
sets := strings.SplitN(s, ";", -1)
// Number of sets can vary, we need to have that info
numSets := len(sets)
return sets, numSets
}
func newGameSet(s string) map[string]int {
m := make(map[string]int)
tempNumColor := strings.SplitN(s, ",", -1)
for i := 0; i < len(tempNumColor); i++ {
TrimmedNumColor := strings.Trim(tempNumColor[i], " ")
NumColor := strings.SplitN(TrimmedNumColor, " ", -1)
m[NumColor[1]], _ = strconv.Atoi(NumColor[0])
}
return m
}
func CheckGame(mySet map[string]int, p *possibleGame) {
if (mySet["red"] > requiredCubes["red"]) {
p.redIsPossible = false
}
if (mySet["green"] > requiredCubes["green"]) {
p.greenIsPossible = false
}
if (mySet["blue"] > requiredCubes["blue"]) {
p.blueIsPossible = false
}
}
func findMinimumSet(mySet map[string]int, m *minimumSet) {
if (mySet["red"] > m.red) {
m.red = mySet["red"]
}
if (mySet["green"] > m.green) {
m.green = mySet["green"]
}
if (mySet["blue"] > m.blue) {
m.blue = mySet["blue"]
}
}
func PossibleGamesSum(s string, gn int, gt *int) {
// Initialize everything to true. If everything was set to false,
// we would have to check in both directions for every pass
isPossible := possibleGame{redIsPossible: true,
greenIsPossible: true,
blueIsPossible: true}
// We will pass a pointer so we can go one map at a time and
// still maintain the results
var isPossiblePoint *possibleGame
isPossiblePoint = &isPossible
// We receive a string with the sets, not split
// We proceed to split
sets, numSets := splitSets(s)
// We received a []string with sets and the number of sets
// Now it's time to create a map
var mySet map[string]int
// For every set we have in the current game
for i := 0; i < numSets; i++ {
// We create a map
mySet = newGameSet(sets[i])
// We check if the game is possible
CheckGame(mySet, isPossiblePoint)
}
if (isPossible.redIsPossible == true) &&
(isPossible.greenIsPossible == true) &&
(isPossible.blueIsPossible == true) {
*gt += gn
}
}
func PowerSetsSum(s string, pt *int) {
// In this case, we need an array of integers
minSet := minimumSet{red: 0, green: 0, blue: 0}
var minSetPoint *minimumSet
minSetPoint = &minSet
// We receive a string with the sets, not split
// We proceed to split
sets, numSets := splitSets(s)
// We received a []string with sets and the number of sets
// Now it's time to create a map
var mySet map[string]int
// For every set we have in the current game
for i := 0; i < numSets; i++ {
// We create a map
mySet = newGameSet(sets[i])
// We find the minimum set
findMinimumSet(mySet, minSetPoint)
}
*pt += (minSet.red * minSet.green * minSet.blue)
}
func PrintAndWait[T any](x T) {
fmt.Print(x)
fmt.Scanln()
}
func main() {
file, err := os.Open("./inputs/day02_input")
check(err)
defer file.Close()
// This variable will hold the game number
var gameNum int = 0
// This variable will hold the pointer of the sum of possible games
var gameSumPoint *int
gameSum := 0
gameSumPoint = &gameSum
// Variable for the sum of the power of the sets
var power int = 0
// Pointer to power, nice variable name
var powerPoint *int = &power
_ = gameNum
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// Split the string in "Game N" and "cubes color"
gameAndCubes := strings.Split(line, ":")
// At this point, gameAndCubes[0] has "Game N"
// We convert the number that remains after replacing "Game " with ""
gameNum, _ = strconv.Atoi(strings.Replace(gameAndCubes[0], "Game ", "", 1))
// Now, for every game, split the sets
PossibleGamesSum(gameAndCubes[1], gameNum, gameSumPoint)
PowerSetsSum(gameAndCubes[1], powerPoint)
}
fmt.Printf("The sum of possible games is: %d\n", gameSum)
fmt.Printf("The sum of the power of sets is: %d\n", power)
}

View File

@ -1,220 +0,0 @@
package main
import (
"fmt"
// "strings"
"bufio"
"os"
"regexp"
"strconv"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x)
fmt.Scanln()
}
func MatchNumbers(lines []string, index int, ast []int, total *int) {
// Regex that finds the numbers in a row
renum := regexp.MustCompile("[0-9]+")
// Gather numbers in prev line
prevLine := renum.FindAllStringIndex(lines[index-1], -1)
// Gather numbers in this line
thisLine := renum.FindAllStringIndex(lines[index], -1)
// Gather numbers in next line
nextLine := renum.FindAllStringIndex(lines[index+1], -1)
// Calculate the number of numbers in three lines
totalNumbers := len(prevLine) + len(thisLine) + len(nextLine)
// Now we create a big array with all the indexes
allIndexes := prevLine
for i := range thisLine {
allIndexes = append(allIndexes, thisLine[i])
}
for i := range nextLine {
allIndexes = append(allIndexes, nextLine[i])
}
// Now we create a big array with all the numbers
// We start from the previous line
allNumbers := renum.FindAllString(lines[index-1], -1)
thisNums := renum.FindAllString(lines[index], -1)
for i := range thisNums {
allNumbers = append(allNumbers, thisNums[i])
}
nextNums := renum.FindAllString(lines[index+1], -1)
for i := range nextNums {
allNumbers = append(allNumbers, nextNums[i])
}
// When we start, we have zero matches
matches := 0
// We will stop when we encounter two numbers
twoNums := [2]int{0, 0}
_ = twoNums
// Cycling through all numbers, but stopping at two matches
for i := 0; i < totalNumbers && matches < 2; i++ {
if (ast[0] >= allIndexes[i][0] - 1 && ast[0] <= allIndexes[i][1]) {
matches += 1
num, _ := strconv.Atoi(allNumbers[i])
twoNums[matches - 1] = num
}
}
if(matches == 2) {
tempGears := twoNums[0] * twoNums[1]
*total += tempGears
}
}
func CheckGears(lines []string) {
total := 0
totalPoint := &total
// Regex that finds the numbers in a row
//renum := regexp.MustCompile("[0-9]+")
// Regex that finds the asterisks in a row
resym := regexp.MustCompile("[*]")
// For every line starting from the second
for i := 0; i < len(lines) - 1; i++ {
// Take the index of the asterisks
asteriskIndex := resym.FindAllStringIndex(lines[i], - 1)
// For every index we get
for j := range asteriskIndex {
MatchNumbers(lines, i, asteriskIndex[j], totalPoint)
}
}
// firstLineNums := renum.FindAllStringIndex(lines[index], -1)
// firstLineSymbolsIndex := resym.FindAllStringIndex(lines[index], -1)
// secondLineSymbolsIndex := resym.FindAllStringIndex(lines[index + 1], -1)
// For every *number index range*, check if in the same line there is a
// symbol on (first - 1) or (last + 1), check in other lines if there is
// a symbol in a specific interval of numbers. If you find a match, you
// can break as you just need one symbol
fmt.Printf("Total of gears is: %d\n", total)
}
func main() {
file, err := os.Open("./inputs/day03_input")
check(err)
defer file.Close()
// This regex find the numbers inside strings
renum := regexp.MustCompile("[0-9]+")
resym := regexp.MustCompile("[^0-9.]+")
scanner := bufio.NewScanner(file)
var lines []string
var totalSum int = 0
// Read the whole file into an array of strings
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
numLines := len(lines)
numbers := make([][]int, numLines)
// The 2D array of numbers will hold all the numbers to easily
// match them with corresponding strings in the file using the index
// For every line in the file, create an array of numbers
for i := 0; i < numLines; i++ {
tempNums := renum.FindAllString(lines[i], -1)
for j := 0; j < len(tempNums); j++ {
num, _ := strconv.Atoi(tempNums[j])
numbers[i] = append(numbers[i], num)
}
}
/* Line [0] needs to check only line 1 for symbols.
Similarly, the last line needs to check only
(last line index - 1) for symbols.
So we first check the line [0], then start a loop
from 1 to (last line index - 1)
*/
firstLineNums := renum.FindAllStringIndex(lines[0], -1)
firstLineSymbolsIndex := resym.FindAllStringIndex(lines[0], -1)
secondLineSymbolsIndex := resym.FindAllStringIndex(lines[1], -1)
// For every *number index range*, check if in the same line there is a
// symbol on (first - 1) or (last + 1), check in other lines if there is
// a symbol in a specific interval of numbers. If you find a match, you
// can break as you just need one symbol
for i := range firstLineNums {
for j := range firstLineSymbolsIndex {
if firstLineSymbolsIndex[j][0] >= firstLineNums[i][0] - 1 &&
(firstLineSymbolsIndex[j][0] <= firstLineNums[i][1]) {
totalSum += numbers[0][i]
break
}
}
for j := range secondLineSymbolsIndex {
if (secondLineSymbolsIndex[j][0] >= firstLineNums[i][0] - 1) &&
(secondLineSymbolsIndex[j][0] <= firstLineNums[i][1]) {
totalSum += numbers[0][i]
break
}
}
}
// Now we loop from 1 to (last index - i)
for i := 1; i < len(lines) - 1; i++ {
// We need to check the current line against an interval of three lines
// breaking the loop for a single number as soon as we find a match
// (we don't want duplicate matches)
currentLineNums := renum.FindAllStringIndex(lines[i], -1)
previousLineIndex := resym.FindAllStringIndex(lines[i - 1], -1)
currentLineIndex := resym.FindAllStringIndex(lines[i], -1)
nextLineIndex := resym.FindAllStringIndex(lines[i + 1], -1)
OuterLoop:
for k := range currentLineNums {
for j := range previousLineIndex {
if previousLineIndex[j][0] >= currentLineNums[k][0] - 1 &&
previousLineIndex[j][0] <= currentLineNums[k][1] {
totalSum += numbers[i][k]
continue OuterLoop
}
}
for j := range currentLineIndex {
if currentLineIndex[j][0] >= currentLineNums[k][0] - 1 &&
currentLineIndex[j][0] <= currentLineNums[k][1] {
totalSum += numbers[i][k]
continue OuterLoop
}
}
for j := range nextLineIndex {
if nextLineIndex[j][0] >= currentLineNums[k][0] - 1 &&
nextLineIndex[j][0] <= currentLineNums[k][1] {
totalSum += numbers[i][k]
continue OuterLoop
}
}
}
}
// Now we need to loop the last line and confront it with previous
// and itself
lastLineNums := renum.FindAllStringIndex(lines[len(lines) - 1], -1)
lastLineSymbolsIndex := resym.FindAllStringIndex(lines[len(lines) - 1], -1)
notLastLineSymbolsIndex := resym.FindAllStringIndex(lines[len(lines) - 2], -1)
// For every *number index range*, check if in the same line there is a
// symbol on (last - 1) or (last + 1), check in other lines if there is
// a symbol in a specific interval of numbers. If you find a match, you
// can break as you just need one symbol
for i := range lastLineNums {
for j := range lastLineSymbolsIndex {
if lastLineSymbolsIndex[j][0] >= lastLineNums[i][0] - 1 &&
(lastLineSymbolsIndex[j][0] <= lastLineNums[i][1]) {
totalSum += numbers[len(lines) - 1][i]
break
}
}
for j := range notLastLineSymbolsIndex {
if (notLastLineSymbolsIndex[j][0] >= lastLineNums[i][0] - 1) &&
(notLastLineSymbolsIndex[j][0] <= lastLineNums[i][1]) {
totalSum += numbers[len(lines) - 1][i]
break
}
}
}
fmt.Printf("The total sum is: %d\n", totalSum)
CheckGears(lines)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,115 +0,0 @@
package main
import (
"fmt"
"strings"
"bufio"
"os"
// "strconv"
"regexp"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x)
fmt.Scanln()
}
func SplitSets(s []string) ([]string, []string) {
// Split the two sets into winning and my numbers
tempwinningNumbers := s[0]
tempMyNumbers := s[1]
// Regex to populate a string with numbers
renum := regexp.MustCompile("[0-9]+")
myNumbers := renum.FindAllString(tempMyNumbers, -1)
winningNumbers := renum.FindAllString(tempwinningNumbers, -1)
return myNumbers, winningNumbers
}
func FindMatches(myNum []string, winNum []string) int {
matches := 0
for i := range myNum {
for j := range winNum {
if (myNum[i] == winNum[j]) {
matches += 1
continue
}
}
}
return matches
}
func CalcTickets(s []string, index int, tpr []int ) {
myNumbers, winningNumbers := SplitSets(s)
matches := FindMatches(myNumbers, winningNumbers)
if (matches > 0) {
for j := 0; j < tpr[index]; j++ {
for i := index; i < index + matches; i++ {
tpr[i+1] += 1
}
}
}
}
func CalcScore (s []string, t *int) {
myNumbers, winningNumbers := SplitSets(s)
matches := FindMatches(myNumbers, winningNumbers)
if(matches > 0) {
tempTotal := 1
for i := 0; i < matches - 1; i++ {
tempTotal *= 2
}
*t += tempTotal
}
}
func main() {
file, err := os.Open("./inputs/day04_input")
check(err)
defer file.Close()
// The total is a simple sum
var total int = 0
var totalPoint *int = &total
// To keep track of the amount of tickets, an array as long
// as the number of lines
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
ticketsPerRow := make([]int, len(lines))
tprPoint := ticketsPerRow[0:len(lines)]
for i := range ticketsPerRow {
ticketsPerRow[i] = 1
}
// Scan every line
for i := 0; i < len(lines); i++ {
// e.g.: Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
cardAndNumbers := strings.Split(lines[i], ":")
// At this point, cardAndNumbers has "Card N"
// We don't need this information (yet?)
allNumbers := strings.Split(cardAndNumbers[1], "|")
CalcScore(allNumbers, totalPoint)
CalcTickets(allNumbers, i, tprPoint)
}
numTickets := 0
for i := range ticketsPerRow {
numTickets += ticketsPerRow[i]
}
fmt.Printf("The scratchcards are worth %d points.\n", total)
fmt.Printf("In total, I have %d scratchcards.", numTickets)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,158 +0,0 @@
package main
import (
"fmt"
// "strings"
"bufio"
"math"
"os"
"strconv"
"regexp"
"sync"
)
type Minimum struct {
mu sync.Mutex
minimum int
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
func GetMaps(scanner *bufio.Scanner, re *regexp.Regexp) [][]int {
// Scan until there is an empty line
var tempNums int = 0
tempArray := make([][]int, 0)
for i := 0; scanner.Scan() && scanner.Text() != ""; i++ {
tempString := re.FindAllString(scanner.Text(), -1)
temp := make([]int, 0)
for j := range tempString {
tempNums, _ = strconv.Atoi(tempString[j])
temp = append(temp, tempNums)
}
tempArray = append(tempArray, temp)
}
// Prepare for next line
scanner.Scan()
return tempArray
}
func (min *Minimum) ParallelMinimum(start, finish int, atrocity [][][]int, wg *sync.WaitGroup) {
// We check the array one by one, need a temp array because
// SeedToLocation wants it
tempNum := make([]int, 1)
tempMin := []int{0}
_ = tempMin
for i := 0; i < finish; i++ {
tempNum[0] = start + i
tempMin := SeedToLocation(tempNum, atrocity)
// Need to modify a shared variable, lock
min.mu.Lock()
if tempMin[0] < min.minimum {
min.minimum = tempMin[0]
}
min.mu.Unlock()
}
// We finished with the Goroutine
wg.Done()
}
func SeedToLocation(seeds []int, atrocity [][][]int) []int {
tempRes := seeds
for i := range atrocity {
tempRes = NextResource(tempRes, atrocity[i])
}
return tempRes
}
func NextResource(previous []int, resource [][]int) []int {
tempRes := make([]int, 0)
// [0] is dest, [1] is source, [2] is range
for i := range previous {
for j := range resource {
if previous[i] >= resource[j][1] &&
previous[i] <= (resource[j][1] + resource[j][2] - 1) {
tempRes = append(tempRes, previous[i] + (resource[j][0] - resource[j][1]))
}
}
// If we didn't add an element to the array
if len(tempRes) == i {
tempRes = append(tempRes, previous[i])
}
}
return tempRes
}
func main () {
file, err := os.Open("./inputs/day05_input")
check(err)
defer file.Close()
min := Minimum{
minimum: math.MaxInt,
}
var wg sync.WaitGroup
// Regex that finds the numbers in a row
renum := regexp.MustCompile("[0-9]+")
var seeds []int
var soils, fertilizers, waters, lights, temperatures,
humidities, locations [][]int
scanner := bufio.NewScanner(file)
// We know that the seeds only have one row
scanner.Scan()
// Put all the numbers in an array of strings
seedsNums := renum.FindAllString(scanner.Text(), -1)
// Extract every number from the string
for i := 0; i < len(seedsNums); i++ {
num, _ := strconv.Atoi(seedsNums[i])
seeds = append(seeds, num)
}
// We know we have an empty string and just a title, skip them
scanner.Scan()
scanner.Scan()
// Should be possible to just pass the scanner
soils = GetMaps(scanner, renum)
fertilizers = GetMaps(scanner, renum)
waters = GetMaps(scanner, renum)
lights = GetMaps(scanner, renum)
temperatures = GetMaps(scanner, renum)
humidities = GetMaps(scanner, renum)
locations = GetMaps(scanner, renum)
tempRes := make([]int, 0)
// Actually insane behaviour
monster := [][][]int{
soils, fertilizers, waters,
lights, temperatures, humidities,
locations,
}
// Send the seeds, receive
tempRes = SeedToLocation(seeds, monster)
minimum := math.MaxInt
for i := range tempRes {
if tempRes[i] < minimum {
minimum = tempRes[i]
}
}
fmt.Printf("Minimum of first part: %d\n", minimum)
// Actual madness
for i := 0; i < len(seeds); i += 2 {
wg.Add(1)
go min.ParallelMinimum(seeds[i], seeds[i+1], monster, &wg)
}
wg.Wait()
//tempRes = SeedToLocation(allSeeds, monster)
fmt.Printf("Minimum of second part: %d\n", min.minimum)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,126 +0,0 @@
package main
import (
"fmt"
// "strings"
"bufio"
// "math"
"os"
"strconv"
"regexp"
"sync"
)
type Race struct {
mu sync.Mutex
total int
}
func (r *Race) WeRaceBoys(time, distance int, wg *sync.WaitGroup) {
// As we just need to find the minimum necessary and then subtract it from
// the maximum, probably a good idea starting form the middle
tempTime := 0
for i := int(time/2); i > 0; i-- {
tempDist := i * (time - i)
if tempDist <= distance {
tempTime = i + 1
break
}
}
// If the minimum is tempTime, then the maximum is time - tempTime
// time - 2*tempTime is the number of possible victories
r.mu.Lock()
r.total *= (time - (2 * tempTime - 1))
r.mu.Unlock()
wg.Done()
}
func MultRaceDist(time, dist []string) ([]int, []int){
tempT, tempD := make([]int, 0), make([]int, 0)
for i := range time {
num, _ := strconv.Atoi(time[i])
tempT = append(tempT, num)
num, _ = strconv.Atoi(dist[i])
tempD = append(tempD, num)
}
return tempT, tempD
}
func SingleRaceDist(time, dist []string) (int, int) {
strT, strD := "", ""
numT, numD := 0, 0
// Create two big strings
for i := range time {
strT += time[i]
strD += dist[i]
}
numT, _ = strconv.Atoi(strT)
numD, _ = strconv.Atoi(strD)
return numT, numD
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
func main() {
file, err := os.Open("./inputs/day06_input")
check(err)
defer file.Close()
// Struct for multiple races
rMult := Race{
total: 1,
}
// Struct for a single race
rSing := Race{
total: 1,
}
_ = &rSing
var wg sync.WaitGroup
// Regex that finds the numbers in a row
renum := regexp.MustCompile("[0-9]+")
scanner := bufio.NewScanner(file)
time, distance := make([]int, 0), make([]int, 0)
tempStrings := make([]string, 0)
// Generic, would work for more rows
for scanner.Scan() {
tempStrings = append(tempStrings, scanner.Text())
}
// Now populate time and distance
timeStr := renum.FindAllString(tempStrings[0], - 1)
distStr := renum.FindAllString(tempStrings[1], - 1)
time, distance = MultRaceDist(timeStr, distStr)
// E.g.: if I hold the button for 1ms and then release it, it will travel at
// 1mm/ms fo the remaining amount of seconds.
// We can skip the holding down 0 and tMAX ms.
// Once we find the MIN amount of ms necessary to win, the limit is
// MAX - MIN
wg.Add(len(time))
for i := 0; i < len(time); i++ {
go rMult.WeRaceBoys(time[i], distance[i], &wg)
}
// Silly implementation of the single race
singT, singD := SingleRaceDist(timeStr, distStr)
wg.Add(1)
go rSing.WeRaceBoys(singT, singD, &wg)
wg.Wait()
fmt.Printf("Multiple races result: %d.\n", rMult.total)
fmt.Printf("Single race result: %d.\n", rSing.total)
}

View File

@ -1,366 +0,0 @@
package main
import (
"bufio"
"fmt"
"math"
"strings"
"os"
"strconv"
"sync"
)
var mapSeedsFirst = map[string]int{
"A": 12,
"K": 11,
"Q": 10,
"J": 9,
"T": 8,
"9": 7,
"8": 6,
"7": 5,
"6": 4,
"5": 3,
"4": 2,
"3": 1,
"2": 0,
}
var mapSeedsSecond = map[string]int{
"A": 12,
"K": 11,
"Q": 10,
"T": 9,
"9": 8,
"8": 7,
"7": 6,
"6": 5,
"5": 4,
"4": 3,
"3": 2,
"2": 1,
"J": 0,
}
type Game struct {
mu sync.Mutex
ranks []int
// 0: High card, 1: One pair, 2: Two pair, 3: Three of a kind
// 4: Full house, 5: Four of a kind, 6: Five of a kind
typeOfHand [7][]string
indexOfHand [7][]int
baseThirteen [7][]int
}
func (g *Game) ChangeBase(hType, index int, mapSeeds map[string]int, wg *sync.WaitGroup) {
// Starting from the first char [0], we create the base13 num
chars := len(g.typeOfHand[hType][index])
baseTN := g.typeOfHand[hType][index]
decNum := 0
for i := 0; i < chars; i++ {
// This should be refactored to be a bit more legible
// It just computes N * 13^i and adds it over
decNum += int(float64(mapSeeds[string(baseTN[i])]) * math.Pow(13, float64(chars-i)))
}
g.baseThirteen[hType][index] = decNum
wg.Done()
}
func (g *Game) AnalyzeMap(cards string, index, mapSize, offset int) {
// The offset defaults to 0 for the regular game
// it is +1 for a game with Jokers\
mapSeed := mapSeedsFirst
if offset != 0 {
mapSeed = mapSeedsSecond
}
switch mapSize {
// Five of a kind
case 1:
g.mu.Lock()
g.typeOfHand[6] = append(g.typeOfHand[6], cards)
g.indexOfHand[6] = append(g.indexOfHand[6], index)
g.mu.Unlock()
// Four of a kind || Full House
case 2:
i := FullOrFour(cards, offset, mapSeed)
g.mu.Lock()
g.typeOfHand[i] = append(g.typeOfHand[i], cards)
g.indexOfHand[i] = append(g.indexOfHand[i], index)
g.mu.Unlock()
// Three of a kind || Two pair
case 3:
i := ThreeOrTwo(cards, offset, mapSeed)
g.mu.Lock()
g.typeOfHand[i] = append(g.typeOfHand[i], cards)
g.indexOfHand[i] = append(g.indexOfHand[i], index)
g.mu.Unlock()
// One pair
case 4:
g.mu.Lock()
g.typeOfHand[1] = append(g.typeOfHand[1], cards)
g.indexOfHand[1] = append(g.indexOfHand[1], index)
g.mu.Unlock()
// High card
case 5:
g.mu.Lock()
g.typeOfHand[0] = append(g.typeOfHand[0], cards)
g.indexOfHand[0] = append(g.indexOfHand[0], index)
g.mu.Unlock()
}
}
func (g *Game) DetermineType(cards string, index int, wg *sync.WaitGroup) {
// We create a map and we check the length. Depending on the length, we
// insert the string in a specific type
m := make(map[string]int)
for i := 0; i < len(cards); i++ {
key := string(cards[i])
m[key] = mapSeedsFirst[key]
}
// Now, depending on the number of elements in the map, we can assign
// append cards to a specific rank
mapSize := len(m)
g.AnalyzeMap(cards, index, mapSize, 0)
wg.Done()
}
func (g *Game) SecondGame(cards string, index int, wg *sync.WaitGroup) {
// If I don't have Js, run the standard DetermineType
m := make(map[string]int)
n := make(map[string]int)
for i := 0; i < len(cards); i++ {
key := string(cards[i])
// We need to track the number of Js
m[key] += 1
// This is to track the type of hand, knowing the number of Js
n[key] = mapSeedsSecond[key]
}
mapSize := len(n)
switch m["J"] {
case 0:
// We have a hand without Js
wg.Add(1)
g.DetermineType(cards, index, wg)
case 1:
// If there is a J, J can be use is not adding information, therefore -1
g.AnalyzeMap(cards, index, (mapSize - 1), 1)
case 2:
g.AnalyzeMap(cards, index, (mapSize - 1), 2)
case 3:
g.AnalyzeMap(cards, index, (mapSize - 1), 3)
case 4:
g.AnalyzeMap(cards, index, (mapSize - 1), 4)
case 5:
wg.Add(1)
g.DetermineType(cards, index, wg)
}
wg.Done()
}
func ThreeOrTwo(cards string, offset int, mapSeed map[string]int) int {
m := make(map[string]int)
for i := 0; i < len(cards); i++ {
key := string(cards[i])
m[key] += 1
}
// If we are in the second game, remove J
if mapSeed["J"] == 0 {
m["J"] = 0
}
// m[i] returns 0 if the element is not in the map. I take advantage
// of that
tempNum := 0
for i := range mapSeed {
if m[i] > tempNum {
tempNum = m[i]
}
}
// If an element has 3 values, we have a three of a kind
if tempNum+offset == 3 {
return 3
/// If an element has 2 values, we have a two pair
} else if tempNum+offset == 2 {
return 2
}
PrintAndWait("This has run for ", cards)
return -1
}
func FullOrFour(cards string, offset int, mapSeed map[string]int) int {
m := make(map[string]int)
for i := 0; i < len(cards); i++ {
key := string(cards[i])
m[key] += 1
}
// If we are in the second game, remove J
if mapSeed["J"] == 0 {
m["J"] = 0
}
// m[i] returns 0 if the element is not in the map. I take advantage
// of that
// Better to save the maximum number
tempNum := 0
for i := range mapSeed {
if m[i] > tempNum {
tempNum = m[i]
}
}
// If an element has four values, we have a Four of a kind
if tempNum+offset == 4 {
return 5
/// If an element has 3 values, we have a Full House
} else if tempNum+offset == 3 {
return 4
}
return -1
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
// https://www.golangprograms.com/golang-program-for-implementation-of-quick-sort.html
// I need to learn how this shing works
func quicksort(a, h []int, hType int) []int {
if len(a) < 2 {
return a
}
left, right := 0, len(a)-1
pivot := 0
a[pivot], a[right] = a[right], a[pivot]
h[pivot], h[right] = h[right], h[pivot]
for i, _ := range a {
if a[i] < a[right] {
a[left], a[i] = a[i], a[left]
h[left], h[i] = h[i], h[left]
left++
}
}
a[left], a[right] = a[right], a[left]
h[left], h[right] = h[right], h[left]
quicksort(a[:left], h[:left], hType)
quicksort(a[left+1:], h[left+1:], hType)
return a
}
func main() {
file, err := os.Open("./inputs/day07_input")
check(err)
defer file.Close()
// Struct for regular game
g := Game{}
// Struct for the Joker game
jo := Game{}
// Variable where we store every line in the file
lines := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
// Array of strings, set of cards
var cards []string
// Array of int, bet
var bet []int
// Now, split the lines
for i := 0; i < len(lines); i++ {
tempString := strings.Split(lines[i], " ")
cards = append(cards, tempString[0])
tempNum, _ := strconv.Atoi(tempString[1])
bet = append(bet, tempNum)
}
// Rank will be from 1 to len(lines)
g.ranks = make([]int, len(lines))
jo.ranks = make([]int, len(lines))
// What do we know for sure? 5 identical seeds are the highest ranks,
// 5 completely different seeds are the lowest ranks.
// We can iterate for every set of cards, and do different things
// if the map we build has one element, five elements or the worst
// case (two, three or four elements).
//
// Two identical: 4oK or FH
// Three identical: 3oK or 22
// Four identical: 12
//
// With this, I put every typeOfHand in a different array
var wg sync.WaitGroup
wg.Add(2 * len(lines))
for i := 0; i < len(lines); i++ {
g.DetermineType(cards[i], i, &wg)
jo.SecondGame(cards[i], i, &wg)
}
wg.Wait()
// Now that g.typeOfHand has every type of hand separated, the rank
// is determined by the first card(s). I can convert every number
// to a base 13 representation and sort.
// In g.indexOfHand I have the index of the corresponding type.
// For every type of hand
for i := range g.typeOfHand {
// As many wait groups as the element we will iterate
wg.Add(len(g.typeOfHand[i]))
wg.Add(len(jo.typeOfHand[i]))
// We also need to initialize the array g.baseThirteen so we can
// keep the same index
g.baseThirteen[i] = make([]int, len(g.typeOfHand[i]))
jo.baseThirteen[i] = make([]int, len(jo.typeOfHand[i]))
// For every element in a single type of hand
for j := range g.typeOfHand[i] {
g.ChangeBase(i, j, mapSeedsFirst, &wg)
}
for j := range jo.typeOfHand[i] {
jo.ChangeBase(i, j, mapSeedsSecond, &wg)
}
}
wg.Wait()
// A sort of some kind. Important is to also move the index with the number as
// well.
for i := range g.baseThirteen {
quicksort(g.baseThirteen[i], g.indexOfHand[i], i)
quicksort(jo.baseThirteen[i], jo.indexOfHand[i], i)
}
curRank := 1
rank := 0
// Iter every array
for i := range g.typeOfHand {
for j := range g.typeOfHand[i] {
index := g.indexOfHand[i][j]
rank += curRank * bet[index]
curRank++
}
}
fmt.Printf("Rank: %d\n", rank)
curRank = 1
rank = 0
// Iter every array
for i := range jo.typeOfHand {
for j := range jo.typeOfHand[i] {
index := jo.indexOfHand[i][j]
rank += curRank * bet[index]
curRank++
}
}
fmt.Printf("Rank: %d\n", rank)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,233 +0,0 @@
package main
import (
"bufio"
"fmt"
"os"
"time"
"regexp"
"sync"
)
const LEFT = 'L'
type Nodes struct {
mu sync.Mutex
commands []int32
singleN []int32
leftN []int32
rightN []int32
index int
steps uint64
allSteps []int
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
// https://siongui.github.io/2017/06/03/go-find-lcm-by-gcd/
// greatest common divisor (GCD) via Euclidean algorithm
func GCD(a, b int) int {
for b != 0 {
t := b
b = a % b
a = t
}
return a
}
// find Least Common Multiple (LCM) via GCD
func LCM(a, b int, integers ...int) int {
result := a * b / GCD(a, b)
for i := 0; i < len(integers); i++ {
result = LCM(result, integers[i])
}
return result
}
func (n *Nodes) toByteSingle(s string) {
// I've just received something like AAA
var temp int32
for i := len(s) - 1; i >= 0; i-- {
a := int32(s[i])
temp += a << ((len(s) - 1 - i) * 8)
}
n.singleN = append(n.singleN, temp)
}
func (n *Nodes) toByteDuet(s, r string) {
// I've just received something like AAA BBB
var tempL, tempR int32
for i := len(s) - 1; i >= 0; i-- {
tempL += int32(s[i]) << ((len(s) - 1 - i) * 8)
tempR += int32(r[i]) << ((len(s) - 1 - i) * 8)
}
n.leftN = append(n.leftN, tempL)
n.rightN = append(n.rightN, tempR)
}
func (n *Nodes) findNext(myN int32) int {
//var wg sync.WaitGroup
ind := 0
for i := 0; i < len(n.singleN); i++ {
if myN^n.singleN[i] == 0 {
n.mu.Lock()
n.index = i
n.mu.Unlock()
ind = i
break
}
}
return ind
}
func (n *Nodes) findAll(ind int, sp []int, wg *sync.WaitGroup) {
index := 0
// We only go from the start
matching := n.rightN[sp[ind]]
if n.commands[0]^LEFT == 0 {
matching = n.leftN[sp[ind]]
}
index = n.findNext(matching)
n.allSteps[ind]++
i := 0
for {
// Every step is in a single direction. For every step, we may need to
// scan len(n.singleN) elements.
// Circular loop
index = n.findNext(matching)
// Increment i after finding the match
i++
i = i % len(n.commands)
// By default, we will assume we are on the right
matching = n.rightN[index]
//PrintAndWait()
// If we are not, we are in the left
if n.commands[i]^LEFT == 0 {
matching = n.leftN[index]
}
n.allSteps[ind]++
// If we find XXZ, end
temp := matching & 255
if temp ^ 'Z' == 0 {
break
}
}
//fmt.Printf("I started from %d, matched at %d, taking %d steps.\n", sp[ind], index, n.allSteps[ind] )
wg.Done()
}
func timer(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}
func main() {
defer timer ("main")()
file, err := os.Open("./inputs/day08_input")
check(err)
defer file.Close()
// Struct with my node
n := Nodes{}
// Prepare the regex
repath := regexp.MustCompile("([A-Z]{3})")
// Build the END
var END = 'Z'
END += ('Z' << 8)
END += ('Z' << 16)
scanner := bufio.NewScanner(file)
scanner.Scan()
// First line, RL commands
strCommands := scanner.Text()
// Get every char inside the string just obtained
for i := 0; i < len(strCommands); i++ {
n.commands = append(n.commands, int32(strCommands[i]))
}
// One empty line
scanner.Scan()
// X = (Y, Z)
// We regex this one
for scanner.Scan() {
tempNodes := repath.FindAllString(scanner.Text(), -1)
n.toByteSingle(tempNodes[0])
n.toByteDuet(tempNodes[1], tempNodes[2])
}
// We start from 0, we find the match
// Let's start an infinite loop
// Circular index
i := 0
// We start from the AAA element
START := 'A'
START += ('A' << 8)
START += ('A' << 16)
matching := START
// Store where AAA is
n.findNext(matching)
// By default, we will assume we are on the right
// If we are not, we are in the left
matching = n.rightN[n.index]
if n.commands[i]^LEFT == 0 {
matching = n.leftN[n.index]
}
n.steps++
// Infinite loop
for {
// Every step is in a single direction. For every step, we may need to
// scan len(n.singleN) elements.
// Circular loop
n.findNext(matching)
// Increment i after finding the match
i++
i = i % len(n.commands)
// By default, we will assume we are on the right
matching = n.rightN[n.index]
//PrintAndWait()
// If we are not, we are in the left
if n.commands[i]^LEFT == 0 {
matching = n.leftN[n.index]
}
n.steps++
// If we find ZZZ, end
if matching^END == 0 {
break
}
}
fmt.Printf("\nSteps: %d\n", n.steps)
// Now, for the main event
// Let's get ready to rumble
startPoints := make([]int, 0)
for i := 0; i < len(n.singleN); i++ {
// Lets remove all bytes except last 8
temp := n.singleN[i] & 255
if (temp ^ 'A') == 0 {
startPoints = append(startPoints, i)
}
}
// Now, from the starting points, we should go and match until
// we find a path that ends in Z
n.allSteps = make([]int, len(startPoints))
var wg sync.WaitGroup
for i := 0; i < len(startPoints); i++ {
wg.Add(1)
go n.findAll(i, startPoints, &wg)
}
wg.Wait()
result := 1
for i := 0; i < len(n.allSteps); i++ {
result = LCM(result, n.allSteps[i])
}
fmt.Printf("Steps: %d\n", result)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,149 +0,0 @@
package main
import(
"fmt"
"os"
"bufio"
"sync"
"time"
"regexp"
"strconv"
)
// Parallel code, global vars
type Series struct {
mu sync.Mutex
numStore [][]int
total uint64
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
// use defer timer("funcname")() when the function you want to
// test starts
func timer(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}
func PredictValueBack(numbers []int) []int {
// Are we finished? By default, true
temp := true
for i := 0; i < len(numbers); i++ {
if numbers[i] != 0 {
temp = false
break
}
}
// Check to end recursion
if temp == true {
return numbers
}
newNums := make([]int, len(numbers) - 1)
for i := 0; i < len(numbers) - 1; i++ {
newNums[i] = numbers[i + 1] - numbers[i]
}
myNums := PredictValueBack(newNums)
addValue := newNums[0] - myNums[0]
// We need to append at the start
newNums = append([]int{addValue}, newNums...)
return newNums
}
func (ser *Series) CallPredictBack(numbers []int, wg *sync.WaitGroup) {
tempNum := PredictValueBack(numbers)
ser.mu.Lock()
ser.total += uint64(numbers[0] - tempNum[0])
ser.mu.Unlock()
wg.Done()
}
func PredictValue(numbers []int) []int {
// Are we finished? By default, true
temp := true
for i := 0; i < len(numbers); i++ {
if numbers[i] != 0 {
temp = false
break
}
}
// Check to end recursion
if temp == true {
return numbers
}
newNums := make([]int, len(numbers) - 1)
for i := 0; i < len(numbers) - 1; i++ {
newNums[i] = numbers[i + 1] - numbers[i]
}
myNums := PredictValue(newNums)
addValue := myNums[len(myNums) - 1]
newNums = append(newNums, newNums[len(newNums) - 1] + addValue)
return newNums
}
func (ser *Series) CallPredict(numbers []int, wg *sync.WaitGroup) {
tempNum := PredictValue(numbers)
lt := len(tempNum) - 1
ln := len(numbers) - 1
ser.mu.Lock()
ser.total += uint64(numbers[ln] + tempNum[lt])
ser.mu.Unlock()
wg.Done()
}
func main() {
defer timer("main")()
file, err := os.Open("./inputs/day09_input")
check(err)
defer file.Close()
var wg sync.WaitGroup
renum := regexp.MustCompile("(\\-[0-9]+|[0-9]+)")
ser := Series{ total: 0, }
lines := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
ser.numStore = make([][]int, len(lines))
for i := 0; i < len(lines); i++ {
temp := renum.FindAllString(lines[i], -1)
for j := 0; j < len(temp); j++ {
num, err := strconv.Atoi(temp[j])
check(err)
ser.numStore[i] = append(ser.numStore[i], num)
}
}
// Now I have a 2D array with all the numbers, I can start RECURSING
wg.Add(len(ser.numStore))
for i := 0; i < len(ser.numStore); i++ {
go ser.CallPredict(ser.numStore[i], &wg)
}
wg.Wait()
fmt.Printf("%d\n", ser.total)
ser.total = 0
wg.Add(len(ser.numStore))
for i := 0; i < len(ser.numStore); i++ {
go ser.CallPredictBack(ser.numStore[i], &wg)
}
wg.Wait()
fmt.Printf("%d\n", ser.total)
}

View File

@ -1 +0,0 @@
../inputs

View File

@ -1,48 +0,0 @@
package main
import (
"bufio"
"fmt"
"os"
"sync"
"time"
)
// Parallel code, global vars
type Nodes struct {
mu sync.Mutex
variable int
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
// use defer timer("funcname")() when the function you want to
// test starts
func timer(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}
func main() {
file, err := os.Open("./inputs")
check(err)
defer file.Close()
lines := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
}

View File

@ -1,42 +0,0 @@
package main
import()
// Parallel code, global vars
type Nodes struct {
mu sync.Mutex
variable type
}
func check(e error) {
if e != nil {
panic(e)
}
}
func PrintAndWait(x ...any) {
fmt.Print(x...)
fmt.Scanln()
}
// use defer timer("funcname")() when the function you want to
// test starts
func timer(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}
func main() {
file, err := os.Open("./inputs")
check(err)
defer file.Close()
lines := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
}