<commit_before># constants
qt  = Math.PI / 2
tau = Math.PI * 2

# general handle on everything; keeps track of geometry of entities it contains
class Universe
  # default initialization parameters
  defaults: ->
    width:  500
    height: 500
    cell:   20
    pause:  10
    maxDistance: 60
    initialCreatures:
      stones: num: 40
      plants: num: 60
      herbivores: num: 8
      carnivores: num: 14
  # set the universe
  constructor: ( id, options = {} ) ->
    @canvas = document.getElementById id
    throw "I have no canvas!" unless @canvas
    @ctx = @canvas.getContext '2d'
    @options = options # keeping this around for some reason; probably unnecessary
    @width = @canvas.width
    @height = @canvas.height
    @maxDim = Math.max @width, @height
    @maxDistance = options.maxDistance || Math.round( Math.max( @width, @height ) / 3 )

    # divide the universe into cells
    @cellWidth = options.cell || @defaults().cell
    @cells = []
    cellBuffer = []
    x = 0
    while x <= @width
      y = 0
      @cells.push column = []
      while y <= @height
        c = new Cell @, x, y, @cellWidth
        o.introduce c for o in cellBuffer
        cellBuffer.push c
        column.push c
        y += @cellWidth
      x += @cellWidth

    @thingCount = 0
    @geoPool = [ 0 ]
    @geoData = [ null ]
    @dotCache = {}
    @idBase = 0
    @tick   = 0
    @pause = options.pause || defaults().pause 
    @callback = options.callback || ->
    dic = @defaults().initialCreatures
    ic = @options.initialCreatures || dic
    uni = @
    used = {}

    # geometry mechanism
    @geo = {
      # memory allocator
      data: =>
        if @geoPool.length > 1
          offset = @geoPool.pop()
        else
          offset = @geoData.length
          @geoData.push null for i in [1..4]
        offset
      # free memory -- useful for debugging
      free: (offset) =>
        @geoPool.push offset
      calc: ( offset, x, y, maxDistance, distance ) =>
        if distance?
          sine = x
          cosine = y
        else
          unless tooFar = Math.abs(x) > maxDistance || Math.abs(y) > maxDistance
            distance = Math.sqrt( x * x + y * y )
            unless tooFar ||= distance > maxDistance
              sine = y / distance
              cosine = x / distance
        unless tooFar
          segment = if sine == 0
            if cosine == 1 then 0 else 4
          else if sine == 1
            2
          else if sine == -1
            6
          else if sine > 0
            if cosine > 0 then 1 else 3
          else
            if cosine > 0 then 7 else 5
        if tooFar
          @geoPool.push offset
          0
        else
          @geoData[ offset ]     = distance
          @geoData[ offset + 1 ] = sine
          @geoData[ offset + 2 ] = cosine
          @geoData[ offset + 3 ] = segment
          offset
      le: ( offset, other ) =>
        s1 = @geoData[ offset + 3 ]
        s2 = @geoData[ other + 3 ]
        if s1 == s2
          if s1 % 2 == 0
            true
          else
            sine1 = @geoData[ offset + 1 ]
            sine2 = @geoData[ offset + 1 ]
            if s1 == 1 || s1 == 7 then sine1 <= sine2 else sine1 >= sine2
        else
          s1 - s2 > 4 || s1 < s2
      ge: ( offset, other ) =>
        @geo.le other, offset
      # produces a vector in the TrigPair direction with the given magnitude
      vector: ( offset, magnitude ) =>
        [ @geoData[ offset + 2 ] * magnitude, @geoData[ offset + 1 ] * magnitude ]
      # generates the counterpart of @ TrigPair after a 180 degree rotation
      opposite: ( offset ) =>
        return offset unless offset
        op = @geo.data()
        @geo.calc( op, -@geoData[ offset + 1 ], -@geoData[ offset + 2 ], null, @geoData[offset] )
    }

    paramsForType = ( type, dontAdd ) ->
      params = dup( ic[type] || {} )['init'] || {}
      params.universe = uni
      params.dontAdd = dontAdd
      params
    createCreatures = ( Cons, type ) ->
      cons = -> new Cons uni.randomLocation(used), paramsForType(type)
      lim = ic[type] && ic[type].num
      lim = dic[type].num unless lim?
      cons() for i in [1..lim] if lim
    createCreatures Stone, 'stones'
    createCreatures Plant, 'plants'
    createCreatures Herbivore, 'herbivores'
    createCreatures Carnivore, 'carnivores'
    # keep an inert instance of each type in its initial state
    @seeds =
      plant:     new Plant [0,0], paramsForType( 'plants', true )
      herbivore: new Herbivore [0,0], paramsForType( 'herbivores', true )
      carnivore: new Carnivore [0,0], paramsForType( 'carnivores', true )
  # iterate over all things in the universe
  visitThings: ( f, returns, safe ) ->
    ret = [] if returns
    for column in @cells
      for cell in column
        if cell.inhabitants.length
          ar = if safe then cell.inhabitants else [].concat cell.inhabitants
          for t in ar
            v = f(t)
            ret.push v if returns
    ret
  thingsAt: ( x, y ) ->
    t for t in @cellAt( x, y ).inhabitants when t.radius > Math.sqrt( (t.x - x)**2 + (t.y - y )**2 )
  cellAt: ( x, y ) -> @cells[ x // @cellWidth ][ y // @cellWidth ]
  # place a thing in the appropriate cell<commit_msg>improved the thingsAt function to get overlapping things centered in neighboring cells<commit_after># constants
qt  = Math.PI / 2
tau = Math.PI * 2

# general handle on everything; keeps track of geometry of entities it contains
class Universe
  # default initialization parameters
  defaults: ->
    width:  500
    height: 500
    cell:   20
    pause:  10
    maxDistance: 60
    initialCreatures:
      stones: num: 40
      plants: num: 60
      herbivores: num: 8
      carnivores: num: 14
  # set the universe
  constructor: ( id, options = {} ) ->
    @canvas = document.getElementById id
    throw "I have no canvas!" unless @canvas
    @ctx = @canvas.getContext '2d'
    @options = options # keeping this around for some reason; probably unnecessary
    @width = @canvas.width
    @height = @canvas.height
    @maxDim = Math.max @width, @height
    @maxDistance = options.maxDistance || Math.round( Math.max( @width, @height ) / 3 )

    # divide the universe into cells
    @cellWidth = options.cell || @defaults().cell
    @cells = []
    cellBuffer = []
    x = 0
    while x <= @width
      y = 0
      @cells.push column = []
      while y <= @height
        c = new Cell @, x, y, @cellWidth
        o.introduce c for o in cellBuffer
        cellBuffer.push c
        column.push c
        y += @cellWidth
      x += @cellWidth

    @thingCount = 0
    @geoPool = [ 0 ]
    @geoData = [ null ]
    @dotCache = {}
    @idBase = 0
    @tick   = 0
    @pause = options.pause || defaults().pause 
    @callback = options.callback || ->
    dic = @defaults().initialCreatures
    ic = @options.initialCreatures || dic
    uni = @
    used = {}

    # geometry mechanism
    @geo = {
      # memory allocator
      data: =>
        if @geoPool.length > 1
          offset = @geoPool.pop()
        else
          offset = @geoData.length
          @geoData.push null for i in [1..4]
        offset
      # free memory -- useful for debugging
      free: (offset) =>
        @geoPool.push offset
      calc: ( offset, x, y, maxDistance, distance ) =>
        if distance?
          sine = x
          cosine = y
        else
          unless tooFar = Math.abs(x) > maxDistance || Math.abs(y) > maxDistance
            distance = Math.sqrt( x * x + y * y )
            unless tooFar ||= distance > maxDistance
              sine = y / distance
              cosine = x / distance
        unless tooFar
          segment = if sine == 0
            if cosine == 1 then 0 else 4
          else if sine == 1
            2
          else if sine == -1
            6
          else if sine > 0
            if cosine > 0 then 1 else 3
          else
            if cosine > 0 then 7 else 5
        if tooFar
          @geoPool.push offset
          0
        else
          @geoData[ offset ]     = distance
          @geoData[ offset + 1 ] = sine
          @geoData[ offset + 2 ] = cosine
          @geoData[ offset + 3 ] = segment
          offset
      le: ( offset, other ) =>
        s1 = @geoData[ offset + 3 ]
        s2 = @geoData[ other + 3 ]
        if s1 == s2
          if s1 % 2 == 0
            true
          else
            sine1 = @geoData[ offset + 1 ]
            sine2 = @geoData[ offset + 1 ]
            if s1 == 1 || s1 == 7 then sine1 <= sine2 else sine1 >= sine2
        else
          s1 - s2 > 4 || s1 < s2
      ge: ( offset, other ) =>
        @geo.le other, offset
      # produces a vector in the TrigPair direction with the given magnitude
      vector: ( offset, magnitude ) =>
        [ @geoData[ offset + 2 ] * magnitude, @geoData[ offset + 1 ] * magnitude ]
      # generates the counterpart of @ TrigPair after a 180 degree rotation
      opposite: ( offset ) =>
        return offset unless offset
        op = @geo.data()
        @geo.calc( op, -@geoData[ offset + 1 ], -@geoData[ offset + 2 ], null, @geoData[offset] )
    }

    paramsForType = ( type, dontAdd ) ->
      params = dup( ic[type] || {} )['init'] || {}
      params.universe = uni
      params.dontAdd = dontAdd
      params
    createCreatures = ( Cons, type ) ->
      cons = -> new Cons uni.randomLocation(used), paramsForType(type)
      lim = ic[type] && ic[type].num
      lim = dic[type].num unless lim?
      cons() for i in [1..lim] if lim
    createCreatures Stone, 'stones'
    createCreatures Plant, 'plants'
    createCreatures Herbivore, 'herbivores'
    createCreatures Carnivore, 'carnivores'
    # keep an inert instance of each type in its initial state
    @seeds =
      stone:     new Stone [0,0], paramsForType( 'stones', true )
      plant:     new Plant [0,0], paramsForType( 'plants', true )
      herbivore: new Herbivore [0,0], paramsForType( 'herbivores', true )
      carnivore: new Carnivore [0,0], paramsForType( 'carnivores', true )
  # obtains the maximum radius of any thing in the universe
  maxRadius: ->
    return @maxRadius unless @maxRadius.nil?
    mx = 0
    for t, o of @seeds
      mx = o.radius if o.radius > mx
    @maxRadius = mx
  # iterate over all things in the universe
  visitThings: ( f, returns, safe ) ->
    ret = [] if returns
    for column in @cells
      for cell in column
        if cell.inhabitants.length
          ar = if safe then cell.inhabitants else [].concat cell.inhabitants
          for t in ar
            v = f(t)
            ret.push v if returns
    ret
  # find all things overlapping (minus appendages) overlapping a given point
  thingsAt: ( x, y ) ->
    c = @cellAt( x, y )
    test = (t) -> t.radius > Math.sqrt( (t.x - x)**2 + (t.y - y )**2 )
    ret = []
    ret.push t for t in c.inhabitants when test t
    for other in c.neighbors when other[1] <= @maxRadius
      ret.push t for t in other[0].inhabitants when test t
    ret
  cellAt: ( x, y ) -> @cells[ x // @cellWidth ][ y // @cellWidth ]
  # place a thing in the appropriate cell
{'fraction_non_alphanumeric': 0.10810321412403803, 'fraction_numerical': 0.017292892711634224, 'mean_word_length': 1.84251158003088, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 58, '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': '2707608', 'n_tokens_mistral': 3517, 'n_tokens_neox': 3333, 'n_words': 1168}