New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught TypeError: Cannot read property 'ctor' of undefined #167

Closed
TheSeamau5 opened this Issue Feb 11, 2015 · 13 comments

Comments

Projects
None yet
2 participants
@TheSeamau5
Contributor

TheSeamau5 commented Feb 11, 2015

Seems like I got a runtime error.

Chrome is pointing at the stepperHelp function in Native.Graphics.Collage. Somehow, something weird got through there and the compiler was silent.

screen shot 2015-02-11 at 2 48 18 am

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Here is the full code of the little project that's causing this:

Model.elm

module Shooter.Model where

import Array (..)

----------
-- MATH --
----------

type alias Point =
  { x : Float
  , y : Float
  }

add : Point -> Point -> Point
add p q =
  Point (p.x + q.x) (p.y + q.y)

square : Float -> Float
square x = x * x

distanceSquared : Point -> Point -> Float
distanceSquared p q =
  square (p.x - q.x) + square (p.y - q.y)

distance : Point -> Point -> Float
distance p q =
  sqrt (distanceSquared p q)


------------
-- ENTITY --
------------

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , radius : Float
  }


collide : Entity a -> Entity b -> Bool
collide entity1 entity2 =
  square (entity1.radius + entity2.radius) <=
  distanceSquared entity1.position entity2.position


move : Entity a -> Entity a
move entity =
  { entity | position <- entity.position `add` entity.velocity }

setVelocity : Point -> Entity a -> Entity a
setVelocity velocity entity =
  { entity | velocity <- velocity }

-------------------
-- GAME ENTITIES --
-------------------

type alias Bullet = Entity { attack : Int }

type alias Ship a =
  Entity { a | health : Int
             , maxHealth : Int }


type alias Hero = Ship {}

hero : Hero
hero =
  { position = Point 200 10
  , velocity = Point 0 0
  , radius = 20
  , health = 10
  , maxHealth = 10
  }

type alias Enemy = Ship {}

enemy : Enemy
enemy =
  { position = Point 200 300
  , velocity = Point 0 0
  , radius = 20
  , health = 1
  , maxHealth = 1
  }

type alias GameState =
  { hero : Hero
  , enemies : Array Enemy
  , bullets : Array Bullet
  }

gameState : GameState
gameState =
  { hero = hero
  , enemies = push enemy empty
  , bullets = empty
  }




isAlive : Ship a -> Bool
isAlive ship =
  ship.health > 0


hit : Bullet -> Ship a -> Ship a
hit bullet ship =
  { ship | health <- ship.health - bullet.attack }


createHeroBullet : Hero -> Int -> Bullet
createHeroBullet hero attack =
  let bulletStartingPosition =
        hero.position `add` Point 0 ((hero.radius / 2) + 2)

      bulletVelocity =
        Point 0 2

      bulletRadius =
        5 * attack

  in
    { position = bulletStartingPosition
    , velocity = bulletVelocity
    , radius = bulletRadius
    , attack = attack
    }

createEnemyBullet : Enemy -> Int -> Bullet
createEnemyBullet enemy attack =
  let bulletStartingPosition =
        enemy.position `add` Point 0 -((enemy.radius / 2) + 2)

      bulletVelocity =
        Point 0 -2

      bulletRadius =
        5 * attack

  in
    { position = bulletStartingPosition
    , velocity = bulletVelocity
    , radius = bulletRadius
    , attack = attack
    }

Input.elm

module Shooter.Input where

import Shooter.Model (..)

import Keyboard
import Signal (..)
import Time (..)


type alias Input =
  { arrows : Point
  , space : Bool
  }


toInput : { x : Int, y : Int} -> Bool -> Input
toInput arrows space =
  { arrows =
    { x = toFloat arrows.x
    , y = toFloat arrows.y
    }
  , space = space
  }


userInput : Signal Input
userInput =
  map2 toInput Keyboard.arrows Keyboard.space

timeDelta : Signal Time
timeDelta =
  fps 60

Render.elm

module Shooter.Render where

import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import Shooter.Model as Model
import Array (..)


renderHero : Model.Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled blue <|
      circle hero.radius

renderEnemy : Model.Enemy -> Form
renderEnemy enemy =
  move (enemy.position.x, enemy.position.y) <|
    filled red <|
      circle enemy.radius


renderBullet : Model.Bullet -> Form
renderBullet bullet =
  move (bullet.position.x, bullet.position.y) <|
    filled green <|
      circle bullet.radius

render : Model.GameState -> Element
render game =
  let enemyForms =
        map renderEnemy game.enemies
      bulletForms =
        map renderBullet game.bullets
      heroForm =
        renderHero game.hero
  in
    collage 400 400
      (toList
        (push heroForm (enemyForms `append` bulletForms)))

Update.elm

module Shooter.Update where

import Shooter.Input (..)
import Shooter.Model (..)

import Array (..)


pipe : (a -> b -> b) -> Array a -> b -> b
pipe f array b =
  case get 0 array of
    Nothing -> b
    Just a ->
      pipe f (slice 1 (length array) array) (f a b)

update : Input -> GameState -> GameState
update input game =
  let bullets =
        if input.space == True
        then
          push (createHeroBullet game.hero 1) game.bullets
        else
          game.bullets
      heroVelocity =
        input.arrows
      applyBullets entity =
        pipe hit
             (filter (collide entity) game.bullets)
             entity
      hero =
        setVelocity heroVelocity
                    (move
                      (applyBullets game.hero))
      enemies =
        map move
            (filter isAlive
                    (map applyBullets game.enemies))


  in
    { game | hero <- hero
           , enemies <- enemies
           , bullets <- bullets
    }

Shooter.elm (the main file)

import Shooter.Model (..)
import Shooter.Input (..)
import Shooter.Update (..)
import Shooter.Render (..)

import Signal (..)

main =
  map render
      (foldp update gameState userInput)
Contributor

TheSeamau5 commented Feb 11, 2015

Here is the full code of the little project that's causing this:

Model.elm

module Shooter.Model where

import Array (..)

----------
-- MATH --
----------

type alias Point =
  { x : Float
  , y : Float
  }

add : Point -> Point -> Point
add p q =
  Point (p.x + q.x) (p.y + q.y)

square : Float -> Float
square x = x * x

distanceSquared : Point -> Point -> Float
distanceSquared p q =
  square (p.x - q.x) + square (p.y - q.y)

distance : Point -> Point -> Float
distance p q =
  sqrt (distanceSquared p q)


------------
-- ENTITY --
------------

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , radius : Float
  }


collide : Entity a -> Entity b -> Bool
collide entity1 entity2 =
  square (entity1.radius + entity2.radius) <=
  distanceSquared entity1.position entity2.position


move : Entity a -> Entity a
move entity =
  { entity | position <- entity.position `add` entity.velocity }

setVelocity : Point -> Entity a -> Entity a
setVelocity velocity entity =
  { entity | velocity <- velocity }

-------------------
-- GAME ENTITIES --
-------------------

type alias Bullet = Entity { attack : Int }

type alias Ship a =
  Entity { a | health : Int
             , maxHealth : Int }


type alias Hero = Ship {}

hero : Hero
hero =
  { position = Point 200 10
  , velocity = Point 0 0
  , radius = 20
  , health = 10
  , maxHealth = 10
  }

type alias Enemy = Ship {}

enemy : Enemy
enemy =
  { position = Point 200 300
  , velocity = Point 0 0
  , radius = 20
  , health = 1
  , maxHealth = 1
  }

type alias GameState =
  { hero : Hero
  , enemies : Array Enemy
  , bullets : Array Bullet
  }

gameState : GameState
gameState =
  { hero = hero
  , enemies = push enemy empty
  , bullets = empty
  }




isAlive : Ship a -> Bool
isAlive ship =
  ship.health > 0


hit : Bullet -> Ship a -> Ship a
hit bullet ship =
  { ship | health <- ship.health - bullet.attack }


createHeroBullet : Hero -> Int -> Bullet
createHeroBullet hero attack =
  let bulletStartingPosition =
        hero.position `add` Point 0 ((hero.radius / 2) + 2)

      bulletVelocity =
        Point 0 2

      bulletRadius =
        5 * attack

  in
    { position = bulletStartingPosition
    , velocity = bulletVelocity
    , radius = bulletRadius
    , attack = attack
    }

createEnemyBullet : Enemy -> Int -> Bullet
createEnemyBullet enemy attack =
  let bulletStartingPosition =
        enemy.position `add` Point 0 -((enemy.radius / 2) + 2)

      bulletVelocity =
        Point 0 -2

      bulletRadius =
        5 * attack

  in
    { position = bulletStartingPosition
    , velocity = bulletVelocity
    , radius = bulletRadius
    , attack = attack
    }

Input.elm

module Shooter.Input where

import Shooter.Model (..)

import Keyboard
import Signal (..)
import Time (..)


type alias Input =
  { arrows : Point
  , space : Bool
  }


toInput : { x : Int, y : Int} -> Bool -> Input
toInput arrows space =
  { arrows =
    { x = toFloat arrows.x
    , y = toFloat arrows.y
    }
  , space = space
  }


userInput : Signal Input
userInput =
  map2 toInput Keyboard.arrows Keyboard.space

timeDelta : Signal Time
timeDelta =
  fps 60

Render.elm

module Shooter.Render where

import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import Shooter.Model as Model
import Array (..)


renderHero : Model.Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled blue <|
      circle hero.radius

renderEnemy : Model.Enemy -> Form
renderEnemy enemy =
  move (enemy.position.x, enemy.position.y) <|
    filled red <|
      circle enemy.radius


renderBullet : Model.Bullet -> Form
renderBullet bullet =
  move (bullet.position.x, bullet.position.y) <|
    filled green <|
      circle bullet.radius

render : Model.GameState -> Element
render game =
  let enemyForms =
        map renderEnemy game.enemies
      bulletForms =
        map renderBullet game.bullets
      heroForm =
        renderHero game.hero
  in
    collage 400 400
      (toList
        (push heroForm (enemyForms `append` bulletForms)))

Update.elm

module Shooter.Update where

import Shooter.Input (..)
import Shooter.Model (..)

import Array (..)


pipe : (a -> b -> b) -> Array a -> b -> b
pipe f array b =
  case get 0 array of
    Nothing -> b
    Just a ->
      pipe f (slice 1 (length array) array) (f a b)

update : Input -> GameState -> GameState
update input game =
  let bullets =
        if input.space == True
        then
          push (createHeroBullet game.hero 1) game.bullets
        else
          game.bullets
      heroVelocity =
        input.arrows
      applyBullets entity =
        pipe hit
             (filter (collide entity) game.bullets)
             entity
      hero =
        setVelocity heroVelocity
                    (move
                      (applyBullets game.hero))
      enemies =
        map move
            (filter isAlive
                    (map applyBullets game.enemies))


  in
    { game | hero <- hero
           , enemies <- enemies
           , bullets <- bullets
    }

Shooter.elm (the main file)

import Shooter.Model (..)
import Shooter.Input (..)
import Shooter.Update (..)
import Shooter.Render (..)

import Signal (..)

main =
  map render
      (foldp update gameState userInput)
@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

I have a sneaking suspicion that it has to do with me using Arrays alongside all this collage stuff... I'm not sure... I have to further investigate

Contributor

TheSeamau5 commented Feb 11, 2015

I have a sneaking suspicion that it has to do with me using Arrays alongside all this collage stuff... I'm not sure... I have to further investigate

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Same bug, smaller code:

Model.elm

module Platformer.Model where

import Array (..)

type alias Point =
  { x : Float
  , y : Float
  }

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , dimensions : Point
  }

type alias Hero =
  Entity { isGrounded : Bool }

hero : Hero
hero =
  { position = Point 0 0
  , velocity = Point 0 0
  , dimensions = Point 10 10
  , isGrounded = True
  }

type alias Platform =
  Entity {}

type alias Game =
  { hero : Hero
  , platforms : Array Platform
  }

game : Game
game =
  { hero = hero
  , platforms = empty
  }

Render.elm

module Platformer.Render where

import Platformer.Model (Game, Hero, Platform)

import Graphics.Collage (..)
import Graphics.Element (..)
import Color (..)
import Array (..)


renderHero : Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled green <|
      rect hero.dimensions.x hero.dimensions.y

renderPlatform : Platform -> Form
renderPlatform platform =
  move (platform.position.x, platform.position.y) <|
    filled blue <|
      rect platform.dimensions.x platform.dimensions.y


render : Game -> Element
render game =
  collage 400 400
    (toList
      (push (renderHero game.hero)
            (map renderPlatform game.platforms)))

Platformer.elm

import Platformer.Model (..)
import Platformer.Render (..)

main = render game
Contributor

TheSeamau5 commented Feb 11, 2015

Same bug, smaller code:

Model.elm

module Platformer.Model where

import Array (..)

type alias Point =
  { x : Float
  , y : Float
  }

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , dimensions : Point
  }

type alias Hero =
  Entity { isGrounded : Bool }

hero : Hero
hero =
  { position = Point 0 0
  , velocity = Point 0 0
  , dimensions = Point 10 10
  , isGrounded = True
  }

type alias Platform =
  Entity {}

type alias Game =
  { hero : Hero
  , platforms : Array Platform
  }

game : Game
game =
  { hero = hero
  , platforms = empty
  }

Render.elm

module Platformer.Render where

import Platformer.Model (Game, Hero, Platform)

import Graphics.Collage (..)
import Graphics.Element (..)
import Color (..)
import Array (..)


renderHero : Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled green <|
      rect hero.dimensions.x hero.dimensions.y

renderPlatform : Platform -> Form
renderPlatform platform =
  move (platform.position.x, platform.position.y) <|
    filled blue <|
      rect platform.dimensions.x platform.dimensions.y


render : Game -> Element
render game =
  collage 400 400
    (toList
      (push (renderHero game.hero)
            (map renderPlatform game.platforms)))

Platformer.elm

import Platformer.Model (..)
import Platformer.Render (..)

main = render game
@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Feb 11, 2015

Member

Is it possible to get it down to one file? To properly debug, it's best if we can get it down to as small as possible. Have we gotten there?

Member

evancz commented Feb 11, 2015

Is it possible to get it down to one file? To properly debug, it's best if we can get it down to as small as possible. Have we gotten there?

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Interesting, when you switch to lists, everything works. So my suspicion seems warranted.

Model.elm

module Platformer.Model where

import List (..)

type alias Point =
  { x : Float
  , y : Float
  }

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , dimensions : Point
  }

type alias Hero =
  Entity { isGrounded : Bool }

hero : Hero
hero =
  { position = Point 0 0
  , velocity = Point 0 0
  , dimensions = Point 10 10
  , isGrounded = True
  }


type alias Platform =
  Entity {}

type alias Game =
  { hero : Hero
  , platforms : List Platform
  }

game : Game
game =
  { hero = hero
  , platforms = []
  }

Render.elm

module Platformer.Render where

import Platformer.Model (Game, Hero, Platform)

import Graphics.Collage (..)
import Graphics.Element (..)
import Color (..)
import List (..)


renderHero : Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled green <|
      rect hero.dimensions.x hero.dimensions.y

renderPlatform : Platform -> Form
renderPlatform platform =
  move (platform.position.x, platform.position.y) <|
    filled blue <|
      rect platform.dimensions.x platform.dimensions.y


render : Game -> Element
render game =
  collage 400 400
    ((::) (renderHero game.hero)
          (map renderPlatform game.platforms))

Platformer.elm

import Platformer.Model (..)
import Platformer.Render (..)

main = render game
Contributor

TheSeamau5 commented Feb 11, 2015

Interesting, when you switch to lists, everything works. So my suspicion seems warranted.

Model.elm

module Platformer.Model where

import List (..)

type alias Point =
  { x : Float
  , y : Float
  }

type alias Entity a =
  { a | position : Point
      , velocity : Point
      , dimensions : Point
  }

type alias Hero =
  Entity { isGrounded : Bool }

hero : Hero
hero =
  { position = Point 0 0
  , velocity = Point 0 0
  , dimensions = Point 10 10
  , isGrounded = True
  }


type alias Platform =
  Entity {}

type alias Game =
  { hero : Hero
  , platforms : List Platform
  }

game : Game
game =
  { hero = hero
  , platforms = []
  }

Render.elm

module Platformer.Render where

import Platformer.Model (Game, Hero, Platform)

import Graphics.Collage (..)
import Graphics.Element (..)
import Color (..)
import List (..)


renderHero : Hero -> Form
renderHero hero =
  move (hero.position.x, hero.position.y) <|
    filled green <|
      rect hero.dimensions.x hero.dimensions.y

renderPlatform : Platform -> Form
renderPlatform platform =
  move (platform.position.x, platform.position.y) <|
    filled blue <|
      rect platform.dimensions.x platform.dimensions.y


render : Game -> Element
render game =
  collage 400 400
    ((::) (renderHero game.hero)
          (map renderPlatform game.platforms))

Platformer.elm

import Platformer.Model (..)
import Platformer.Render (..)

main = render game
@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Nope, I haven't gotten there, but I feel like I'm almost there.

Contributor

TheSeamau5 commented Feb 11, 2015

Nope, I haven't gotten there, but I feel like I'm almost there.

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

AHA!

import Graphics.Collage (filled, square, Form, collage)
import Graphics.Element (Element)
import Array (..)
import Color (green)

renderBox : (Float, Float) -> Form
renderBox (x,y) =
  filled green <|
    square 10

scene : Element
scene =
  collage 400 400
    (toList
      (map renderBox empty))

main : Element
main = scene

There seems to be an issue with mapping renderBox on empty

Contributor

TheSeamau5 commented Feb 11, 2015

AHA!

import Graphics.Collage (filled, square, Form, collage)
import Graphics.Element (Element)
import Array (..)
import Color (green)

renderBox : (Float, Float) -> Form
renderBox (x,y) =
  filled green <|
    square 10

scene : Element
scene =
  collage 400 400
    (toList
      (map renderBox empty))

main : Element
main = scene

There seems to be an issue with mapping renderBox on empty

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Could it be related to this:

When I asText an empty list

import Text (asText)

main =
  asText []

I get this:

screen shot 2015-02-11 at 11 00 45 pm

which is expected.

But if I toList an empty array and asText it

import Text (asText)
import Array (..)

main =
  asText
    (toList
      (map (\x -> x * x) empty))

I get this:

screen shot 2015-02-11 at 11 02 03 pm

which is cryptic. I'd expect to just get the empty list with nothing inside.

In all those cases I was mapping a function over an empty array that was getting converted back to a list. Maybe that could be the problem?

Contributor

TheSeamau5 commented Feb 11, 2015

Could it be related to this:

When I asText an empty list

import Text (asText)

main =
  asText []

I get this:

screen shot 2015-02-11 at 11 00 45 pm

which is expected.

But if I toList an empty array and asText it

import Text (asText)
import Array (..)

main =
  asText
    (toList
      (map (\x -> x * x) empty))

I get this:

screen shot 2015-02-11 at 11 02 03 pm

which is cryptic. I'd expect to just get the empty list with nothing inside.

In all those cases I was mapping a function over an empty array that was getting converted back to a list. Maybe that could be the problem?

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Feb 11, 2015

Member

Quite sketchy. I am not very familiar with the Array implementation, so I would not be terribly surprised if there was some bug there.

Member

evancz commented Feb 11, 2015

Quite sketchy. I am not very familiar with the Array implementation, so I would not be terribly surprised if there was some bug there.

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

Yeah it is.

I think I've found it, the bug is with the Array.map function.

If I convert an empty array to a list and ask for its length:

import Text (asText)
import Array (..)
import List


main =
  asText
    (List.length
      (toList empty))

I get:

screen shot 2015-02-11 at 11 10 34 pm

But, if I first map a function on an empty array and then call toList and ask for its length:

import Text (asText)
import Array (..)
import List


main =
  asText
    (List.length
      (toList
        (map (\x -> x * x) empty)))

I get:

screen shot 2015-02-11 at 11 09 28 pm

Contributor

TheSeamau5 commented Feb 11, 2015

Yeah it is.

I think I've found it, the bug is with the Array.map function.

If I convert an empty array to a list and ask for its length:

import Text (asText)
import Array (..)
import List


main =
  asText
    (List.length
      (toList empty))

I get:

screen shot 2015-02-11 at 11 10 34 pm

But, if I first map a function on an empty array and then call toList and ask for its length:

import Text (asText)
import Array (..)
import List


main =
  asText
    (List.length
      (toList
        (map (\x -> x * x) empty)))

I get:

screen shot 2015-02-11 at 11 09 28 pm

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

I'm not familiar with the Array implementation either, but at least now you know where to look :D

Contributor

TheSeamau5 commented Feb 11, 2015

I'm not familiar with the Array implementation either, but at least now you know where to look :D

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
@TheSeamau5

TheSeamau5 Feb 11, 2015

Contributor

So, I totally just found a quickfix to it and everything works! Yay!

The commit: fbea1ba

Contributor

TheSeamau5 commented Feb 11, 2015

So, I totally just found a quickfix to it and everything works! Yay!

The commit: fbea1ba

@TheSeamau5

This comment has been minimized.

Show comment
Hide comment
Contributor

TheSeamau5 commented Feb 16, 2015

@TheSeamau5 TheSeamau5 closed this Feb 16, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment