ace

joined 2 years ago
[–] ace@lemmy.ananace.dev 7 points 1 week ago (1 children)

The Librelancer project is working on this.

[–] ace@lemmy.ananace.dev 22 points 2 weeks ago (1 children)

There are actually a few projects doing exactly that, at least for the early entries;

  • FreeSO - Open-source version of The Sims: Online but with a bunch of modern improvements, main server shut down at the end of last year
  • Simitone - Single-player interface for FreeSO
  • FreeSims - Open-source engine for The Sims
  • OpenTS2 - Open-source implementation of The Sims 2 engine in Unity

Development pace for them is somewhat slow due apparent lack of interest - and a healthy dose of fear of EA interference - though.

[–] ace@lemmy.ananace.dev 2 points 2 weeks ago (3 children)

Done both, but I've found I rather enjoy the mix of stick and trackpad, emulated as KB+M

[–] ace@lemmy.ananace.dev 10 points 2 weeks ago

Calling it a "Lemmy/Mastodon bridge" sounds off, it's like saying "Gmail/Outlook bridge" when discussing the sending of emails between the two.

I'd use the word "interoperability" instead, or maybe "interaction" for something slightly less technical.

[–] ace@lemmy.ananace.dev 13 points 2 weeks ago (5 children)

I might be slightly biased, but I can also recommend OpenMW for Deck.

[–] ace@lemmy.ananace.dev 43 points 2 months ago (3 children)

Eurofighter Typhoon

[–] ace@lemmy.ananace.dev 61 points 2 months ago (3 children)

Eurofighter Typhoon

[–] ace@lemmy.ananace.dev 36 points 2 months ago (3 children)

Apparently posting it caused enough load to take down my pict-rs server, sorry about that.

 
[–] ace@lemmy.ananace.dev 2 points 2 months ago

Ended up oversleeping somewhat, so I did the first part on the way to work using flood fills over a global visited set, and now that work's over I've sat down to expand that solution to do corner counting for part two as well.

C#

char[] map = new char[0];
(int X, int Y) size = (0, 0);

public void Input(IEnumerable<string> lines)
{
  map = string.Concat(lines).ToCharArray();
  size = (lines.First().Length, lines.Count());
}

Dictionary<HashSet<(int,int)>,int> areas = new Dictionary<HashSet<(int,int)>,int>();
public void PreCalc()
{
  HashSet<(int, int)> visited = new HashSet<(int, int)>();
  for (int y = 0; y < size.Y; ++y)
    for (int x = 0; x < size.X; ++x)
    {
      var at = (x, y);
      if (visited.Contains(at))
        continue;

      var area = Flood((x, y), visited);
      areas[area.Area] = area.Perim;
    }
}

public void Part1()
{
  int sum = areas.Select(kv => kv.Key.Count * kv.Value).Sum();

  Console.WriteLine($"Fencing total: {sum}");
}
public void Part2()
{
  int sum = areas.Select(kv => kv.Key.Count * countCorners(kv.Key)).Sum();

  Console.WriteLine($"Fencing total: {sum}");
}

readonly (int dX, int dY)[] links = new[] { (1, 0), (0, 1), (-1, 0), (0, -1) };
(HashSet<(int,int)> Area, int Perim) Flood((int X, int Y) from, HashSet<(int X, int Y)> visited)
{
  char at = map[from.Y * size.X + from.X];

  (HashSet<(int,int)> Area, int Perim) ret = (new HashSet<(int,int)>(), 0);
  visited.Add(from);
  ret.Area.Add(from);

  foreach (var link in links)
  {
    (int X, int Y) newAt = (from.X + link.dX, from.Y + link.dY);
    char offset ;
    if (newAt.X < 0 || newAt.X >= size.X || newAt.Y < 0 || newAt.Y >= size.Y)
      offset = '\0';
    else
      offset = map[newAt.Y * size.X + newAt.X];

    if (offset == at)
    {
      if (visited.Contains(newAt))
        continue;

      var nextArea = Flood(newAt, visited);
      ret.Area.UnionWith(nextArea.Area);
      ret.Perim += nextArea.Perim;
    }
    else
    {
      ret.Perim += 1;
    }
  }

  return ret;
}

readonly (int dX, int dY)[] cornerPoints = new[] { (0, 0), (1, 0), (1, 1), (0, 1) };
readonly int[] diagonalValues = new[] { (2 << 0) + (2 << 2), (2 << 1) + (2 << 3) };
int countCorners(HashSet<(int X, int Y)> points)
{
  int corners = 0;
  var bounds = findBounds(points);
  for (int y = bounds.minY - 1; y < bounds.maxY + 1; ++y)
    for (int x = bounds.minX - 1; x < bounds.maxX + 1; ++x)
    {
      var atPoint = cornerPoints.Select(c => points.Contains((x + c.dX, y + c.dY)));
      var before = corners;
      if (atPoint.Where(c => c).Count() % 2 == 1)
        corners++;
      else if (diagonalValues.Contains(atPoint.Select((c, i) => c ? (2 << i) : 0).Sum()))
        corners += 2;
    }

  return corners;
}

(int minX, int minY, int maxX, int maxY) findBounds(HashSet<(int X, int Y)> points)
{
  (int minX, int minY, int maxX, int maxY) ret = (int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
  foreach (var point in points)
  {
    ret.minX = Math.Min(ret.minX, point.X);
    ret.minY = Math.Min(ret.minY, point.Y);
    ret.maxX = Math.Max(ret.maxX, point.X);
    ret.maxY = Math.Max(ret.maxY, point.Y);
  }

  return ret;
}

[–] ace@lemmy.ananace.dev 2 points 2 months ago

Well, there's the ALFIS project

[–] ace@lemmy.ananace.dev 3 points 2 months ago

And now we get into the days where caching really is king. My first attempt didn't go so well, I tried to handle the full list result as one cache step, instead of individually caching the result of calculating each stone per step.

I think my original attempt is still calculating at home, but I finished up this much better version on the trip to work.
All hail public transport.

C#

List<long> stones = new List<long>();
public void Input(IEnumerable<string> lines)
{
  stones = string.Concat(lines).Split(' ').Select(v => long.Parse(v)).ToList();
}

public void Part1()
{
  var expanded = TryExpand(stones, 25);

  Console.WriteLine($"Stones: {expanded}");
}
public void Part2()
{
  var expanded = TryExpand(stones, 75);

  Console.WriteLine($"Stones: {expanded}");
}

public long TryExpand(IEnumerable<long> stones, int steps)
{
  if (steps == 0)
    return stones.Count();
  return stones.Select(s => TryExpand(s, steps)).Sum();
}
Dictionary<(long, int), long> cache = new Dictionary<(long, int), long>();
public long TryExpand(long stone, int steps)
{
  var key = (stone, steps);
  if (cache.ContainsKey(key))
    return cache[key];

  var result = TryExpand(Blink(stone), steps - 1);
  cache[key] = result;
  return result;
}

public IEnumerable<long> Blink(long stone)
{
  if (stone == 0)
  {
    yield return 1;
    yield break;
  }
  var str = stone.ToString();
  if (str.Length % 2 == 0)
  {
    yield return long.Parse(str[..(str.Length / 2)]);
    yield return long.Parse(str[(str.Length / 2)..]);
    yield break;
  }
  yield return stone * 2024;
}

[–] ace@lemmy.ananace.dev 2 points 2 months ago

Nice to have a really simple one for a change, both my day 1 and 2 solutions worked on their very first attempts.
I rewrote the code to combine the two though, since the implementations were almost identical for both solutions, and also to replace the recursion with a search list instead.

C#

int[] heights = new int[0];
(int, int) size = (0, 0);

public void Input(IEnumerable<string> lines)
{
  size = (lines.First().Length, lines.Count());
  heights = string.Concat(lines).Select(c => int.Parse(c.ToString())).ToArray();
}

int trails = 0, trailheads = 0;
public void PreCalc()
{
  for (int y = 0; y < size.Item2; ++y)
    for (int x = 0; x < size.Item1; ++x)
      if (heights[y * size.Item1 + x] == 0)
      {
        var unique = new HashSet<(int, int)>();
        trails += CountTrails((x, y), unique);
        trailheads += unique.Count;
      }
}

public void Part1()
{
  Console.WriteLine($"Trailheads: {trailheads}");
}
public void Part2()
{
  Console.WriteLine($"Trails: {trails}");
}

int CountTrails((int, int) from, HashSet<(int,int)> unique)
{
  int found = 0;

  List<(int,int)> toSearch = new List<(int, int)>();
  toSearch.Add(from);

  while (toSearch.Any())
  {
    var cur = toSearch.First();
    toSearch.RemoveAt(0);

    int height = heights[cur.Item2 * size.Item1 + cur.Item1];
    for (int y = -1; y <= 1; ++y)
      for (int x = -1; x <= 1; ++x)
      {
        if ((y != 0 && x != 0) || (y == 0 && x == 0))
          continue;

        var newAt = (cur.Item1 + x, cur.Item2 + y);
        if (newAt.Item1 < 0 || newAt.Item1 >= size.Item1 || newAt.Item2 < 0 || newAt.Item2 >= size.Item2)
          continue;

        int newHeight = heights[newAt.Item2 * size.Item1 + newAt.Item1];
        if (newHeight - height != 1)
          continue;

        if (newHeight == 9)
        {
          unique.Add(newAt);
          found++;
          continue;
        }

        toSearch.Add(newAt);
      }
  }

  return found;
}

 
 

21st of October, let's go!

Available on Steam for wishlisting now as well.
Not sure I agree with having the expansion on the same cost as the base game, but it is a tremendous amount of changes and improvements, both in the free patch as well as the additional paid content. So I'm definitely going to buy it.

 

It's getting close, next week should bring a planned release date.

 
 

Looks like things are going to get really interesting

 

It's nice to see the continued balancing and optimization work that they're doing, and more modding capabilities is always great.

 

Not sure how well bombastic brass will do over longer periods of play, but I'm sure Wube have thought of that - going to be really interesting to see/hear this in action.

 
 
view more: next ›