I like your spirit, but I don't think a Chinese equivalent to Google Play Services would be more desirable
eco_game
I find the Jellyfin webapp a pretty bad experience on mobile, compared to FinDroid.
I really like the webapp on my LG webOS TV (especially good with the Magic Remote) though.
So I guess it kind of depends on the platform.
I actually use both apps, I find Organic Maps a lot nicer for looking at a map and navigating by foot or bike, and Magic Earth seems to pick more sensible car routes some times. Also the live traffic data makes it more fitting for car navigation (Organic Maps doesn't have traffic data).
Jellyfin did some work on integrating the Skip Intros plugin a lot better, AFAIK you just have to enable it once on your server and then once in the settings of all Jellyfin web players.
As for apps, there are some good native third party apps which I mentioned here.
Was this with the first party Jellyfin app or with Swiftfin?
If it was with the first party app, I'd definitely recommend giving Swiftfin a try.
Jellyfin has native apps for Android, Android TV and iOS.
Does Plex offer native apps (that aren't just stripped down browsers) for more platforms?
I can highly recommend Magic Earth as a Google Maps alternative (also available for Android). It uses OSM data and has some traffic info. It's not as good as Google, but it's the closest I've found so far.
AFAIK you can enable the snapcast server and then use an app like SnapCast to stream to your mobile devices.
I also really liked day 17, because at one point the solution for part 2 just clicked and now it makes so much sense.
I think my favorite was the full adder on day 24. Even though I only solved part 2 manually, it was still a lot of fun to reverse an actual full adder and get familiar with all the required logic gates.
I also solved part2 manually today, by outputting the correct addition and program output in binary and then reversing the path for all wrong outputs.
Then I just had to compare that to the formulas for a full adder and I found my swaps pretty quickly that way.
I did all of this in Kotlin and on paper, with a handy helper method to construct the paths.
It's at the very bottom of this file on Github.
I suspect that comparing the bits and then comparing the paths to the full adder formulas can also be done 'automatically' decently easily, but I was too lazy to implement that today.
Kotlin
Part1 was pretty simple, just check all neighbors of a node for overlap, then filter out triples which don't have nodes beginning with 't'.
For part2, I seem to have picked a completely different strategy to everyone else. I was a bit lost, then just boldly assumed, that if I take overlap of all triples with 1 equal node, I might be able to find the answer that way. To my surprise, this worked for my input. I'd be very curious to know if I just got lucky or if the puzzle is designed to work with this approach.
The full code is also on GitHub.
::: spoiler Solution
class Day23 : Puzzle {
private val connections = ArrayList<Pair<String, String>>()
private val tripleCache = HashSet<Triple<String, String, String>>()
override fun readFile() {
val input = readInputFromFile("src/main/resources/day23.txt")
for (line in input.lines()) {
val parts = line.split("-")
connections.add(Pair(parts[0], parts[1]))
}
}
override fun solvePartOne(): String {
val triples = getConnectionTriples(connections)
tripleCache.addAll(triples) // for part 2
val res = triples.count { it.first.startsWith("t") || it.second.startsWith("t") || it.third.startsWith("t") }
return res.toString()
}
private fun getConnectionTriples(connectionList: List<Pair<String, String>>): List<Triple<String, String, String>> {
val triples = ArrayList<Triple<String, String, String>>()
for (connection in connectionList) {
val connectionListTemp = getAllConnections(connection.first, connectionList)
for (i in connectionListTemp.indices) {
for (j in i + 1 until connectionListTemp.size) {
val con1 = connectionListTemp[i]
val con2 = connectionListTemp[j]
if (Pair(con1, con2) in connectionList || Pair(con2, con1) in connectionList) {
val tripleList = mutableListOf(connection.first, con1, con2)
tripleList.sort()
triples.add(Triple(tripleList[0], tripleList[1], tripleList[2]))
}
}
}
}
return triples.distinct()
}
private fun getAllConnections(connection: String, connectionList: List<Pair<String, String>>): List<String> {
val res = HashSet<String>()
for (entry in connectionList) {
when (connection) {
entry.first -> res.add(entry.second)
entry.second -> res.add(entry.first)
}
}
return res.toList()
}
override fun solvePartTwo(): String {
val pools = getPools(connections)
println(pools)
val res = pools.maxByOrNull { it.size }!!
return res.joinToString(",")
}
// will get all pools with a minimum size of 4
// this method makes some naive assumptions, but works for the example and my puzzle input
private fun getPools(connectionList: List<Pair<String, String>>): List<List<String>> {
val pools = ArrayList<List<String>>()
val triples = tripleCache
val nodes = connectionList.map { listOf(it.first, it.second) }.flatten().toHashSet()
for (node in nodes) {
val contenders = triples.filter { it.first == node || it.second == node || it.third == node }
if (contenders.size < 2) continue // expect the minimum result to be 4, for efficiency
// if *all* nodes within *all* triples are interconnected, add to pool
// this may not work for all inputs!
val contenderList = contenders.map { listOf(it.first, it.second, it.third) }.flatten().distinct()
if (checkAllConnections(contenderList, connectionList)) {
pools.add(contenderList.sorted())
}
}
return pools.distinct()
}
private fun checkAllConnections(pool: List<String>, connectionList: List<Pair<String, String>>): Boolean {
for (i in pool.indices) {
for (j in i + 1 until pool.size) {
val con1 = pool[i]
val con2 = pool[j]
if (Pair(con1, con2) !in connectionList && Pair(con2, con1) !in connectionList) {
return false
}
}
}
return true
}
}
You actually can still use it, there's some folks on reddit trying their best to keep it going. App support is very poor though.