<commit_before>export default class PheromoneNetwork {
  public static deserialize(memObj: { layers: { [type: string]: number[] }, roomName: string }): PheromoneNetwork {
    const pn = new PheromoneNetwork(Game.rooms[memObj.roomName])
    for (const type in memObj.layers) { pn.layers[type] = PathFinder.CostMatrix.deserialize(memObj.layers[type]) }
    return pn
  }

  public readonly layers: { [type: string]: CostMatrix }

  private room: Room

  constructor(room: Room) {
    this.room = room
    this.layers = {
      controller: new PathFinder.CostMatrix(),
      energy: new PathFinder.CostMatrix(),
      home: new PathFinder.CostMatrix()
    }
  }

  public diffuse() {
    const rate = 0.01
    const min = 4
    let x: number
    let y: number
    for (const type in this.layers) {
      for (x = 0; x < 50; x++) {
        for (y = 0; y < 50; y++) {
          const curr = this.layers[type].get(x, y)
          if (curr < min) { continue }
          const amt = Math.max(Math.floor(curr * rate), 1)
          const DIRECTION_COORDINATE_DELTAS: { [dir: number]: [number, number] } = {
            8: [-1, -1],  1: [0, -1],  2: [1, -1],
            7: [-1, 0],   /*Center*/   3: [1, 0],
            6: [-1, 1],   5: [0, 1],   4: [1, 1]
          }
          const nearbyTiles: Array<{ amount: number, x: number, y: number}> = []
          for (const dir in DIRECTION_COORDINATE_DELTAS) {
            const [dx, dy] = DIRECTION_COORDINATE_DELTAS[dir]
            const nx = x + dx
            const ny = y + dy
            if (nx < 0 || ny < 0 || nx > 49 || ny > 49) { continue }
            const nearbyCurr = this.layers[type].get(nx, ny)
            if (nearbyCurr >= curr) { continue }
            nearbyTiles.push({ amount: nearbyCurr, x: nx, y: ny })
          }
          if (nearbyTiles.length < 1) { continue }
          const lowestNearby = _(nearbyTiles).shuffle().min(({ amount }) => amount)
          this.layers[type].set(lowestNearby.x, lowestNearby.y, Math.min(lowestNearby.amount + amt, 255))
          this.layers[type].set(x, y, Math.max(curr - amt, 0))
        }
      }
      this.room.memory.pheromoneNetwork.layers[type] = this.layers[type].serialize()
    }
  }

  public dissipate() {
    const rate = 1
    let x: number
    let y: number
    for (const type in this.layers) {
      for (x = 0; x < 50; x++) {
        for (y = 0; y < 50; y++) {
          const curr = this.layers[type].get(x, y)
          if (curr === 0) { continue }
          this.layers[type].set(x, y, Math.max(curr - rate, 0))
        }
      }
      this.room.memory.pheromoneNetwork.layers[type] = this.layers[type].serialize()
    }
  }
<commit_msg>Adjust dissipation to be rate based<commit_after>export default class PheromoneNetwork {
  public static deserialize(memObj: { layers: { [type: string]: number[] }, roomName: string }): PheromoneNetwork {
    const pn = new PheromoneNetwork(Game.rooms[memObj.roomName])
    for (const type in memObj.layers) { pn.layers[type] = PathFinder.CostMatrix.deserialize(memObj.layers[type]) }
    return pn
  }

  public readonly layers: { [type: string]: CostMatrix }

  private room: Room

  constructor(room: Room) {
    this.room = room
    this.layers = {
      controller: new PathFinder.CostMatrix(),
      energy: new PathFinder.CostMatrix(),
      home: new PathFinder.CostMatrix()
    }
  }

  public diffuse() {
    const rate = 0.01
    const min = 4
    let x: number
    let y: number
    for (const type in this.layers) {
      for (x = 0; x < 50; x++) {
        for (y = 0; y < 50; y++) {
          const curr = this.layers[type].get(x, y)
          if (curr < min) { continue }
          const amt = Math.max(Math.floor(curr * rate), 1)
          const DIRECTION_COORDINATE_DELTAS: { [dir: number]: [number, number] } = {
            8: [-1, -1],  1: [0, -1],  2: [1, -1],
            7: [-1, 0],   /*Center*/   3: [1, 0],
            6: [-1, 1],   5: [0, 1],   4: [1, 1]
          }
          const nearbyTiles: Array<{ amount: number, x: number, y: number}> = []
          for (const dir in DIRECTION_COORDINATE_DELTAS) {
            const [dx, dy] = DIRECTION_COORDINATE_DELTAS[dir]
            const nx = x + dx
            const ny = y + dy
            if (nx < 0 || ny < 0 || nx > 49 || ny > 49) { continue }
            const nearbyCurr = this.layers[type].get(nx, ny)
            if (nearbyCurr >= curr) { continue }
            nearbyTiles.push({ amount: nearbyCurr, x: nx, y: ny })
          }
          if (nearbyTiles.length < 1) { continue }
          const lowestNearby = _(nearbyTiles).shuffle().min(({ amount }) => amount)
          this.layers[type].set(lowestNearby.x, lowestNearby.y, Math.min(lowestNearby.amount + amt, 255))
          this.layers[type].set(x, y, Math.max(curr - amt, 0))
        }
      }
      this.room.memory.pheromoneNetwork.layers[type] = this.layers[type].serialize()
    }
  }

  public dissipate() {
    const rate = 0.01
    let x: number
    let y: number
    for (const type in this.layers) {
      for (x = 0; x < 50; x++) {
        for (y = 0; y < 50; y++) {
          const curr = this.layers[type].get(x, y)
          if (curr === 0) { continue }
          const amt = Math.max(Math.floor(curr * rate), 1)
          this.layers[type].set(x, y, Math.max(curr - amt, 0))
        }
      }
      this.room.memory.pheromoneNetwork.layers[type] = this.layers[type].serialize()
    }
  }

{'fraction_non_alphanumeric': 0.14917638349065335, 'fraction_numerical': 0.020914306866555616, 'mean_word_length': 2.1129032258064515, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '109027', 'n_tokens_mistral': 1931, 'n_tokens_neox': 1846, 'n_words': 490}