Join us on Discord!
You can help CodeWalrus stay online by donating here.

Again, regarding help on a game I am working on.

Started by Strontium, May 05, 2015, 12:29:09 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Strontium

I've been trying this issue I have been having for the past few hours, and I am extremely frustrated by it right now.

In the game I am working on, a port of the CLI game 'greed' for Linux. If the player tries to move right when their player is on the very right side of the screen (or if they try to move left when the player is on the very left side of the screen), the game crashes, with this error:

attempt to index field '?' (a nil value)

This happens in on.charIn, where the player movement code is.

Can someone help me with this? I have tried literally everything I can think of and it has not helped.

Current code:

-- colors {{color, style}, {color, style}}
local colors = {
  {"0xFFBF00", 'r'},
  {"0xB31B1B", 'r'},
  {"0x006B3C", 'r'},
  {"0x1560BD", 'r'},
  {"0x872657", 'r'},
  {"0xFFBF00", 'b'},
  {"0xB31B1B", 'b'},
  {"0x006B3C", 'b'},
  {"0x1560BD", 'b'},
}

function on.resize(w,h)
    -- font width & heights
    -- hardcoded because my other method only worked on the emulator for some reason
    strWidth, strHeight = 7,13
   
    -- window width & height
    winWidth, winHeight = w, h
   
    -- window width & height, character wise
    wincharWidth = math.floor(winWidth / strWidth)
    wincharHeight = math.floor(winHeight / strHeight)
   
    reset()
end

function reset()
    score = 0
    var.store('score', score)
    var.unmonitor('score')

    -- player location {x, y}
    player = {math.random(1, wincharWidth), math.random(1, wincharHeight)}
   
    -- generate board {{x, y}, {a, b}}
    board = {}
    for x = 1, wincharWidth do
      board[x] = {}
      for y = 1, wincharHeight do
        board[x][y] = math.random(1, 9)
      end
    end
    board[player[1]][player[2]] = nil
    platform.window:invalidate()
end
on.escapeKey = reset

-- set background color to black
platform.window:setBackgroundColor(0x000000)

-- controls
function on.charIn(char)
  -- WARNING:
  -- I use "notblocked" and "notblocked_" here a lot. I tried to be descriptive, k?

  -- make sure player isn't already moving
  if not isMoving then
    -- find how far and what direction to move the player
    if char == '1' then
      notBlocked, notBlocked_ = pcall(function() return board[player[1] - 1][player[2] + 1] end)
      print(notBlocked, notBlocked_)
      if not notBlocked or notBlocked_ then
        distance = board[player[1] - 1][player[2] + 1]
        destination = {player[1] - distance, player[2] + distance}

        isMoving = true
     
        -- make sure move is valid
        for i = 1, distance do
          notBlocked, notBlocked_ = pcall(function() return board[player[1] - i][player[2] + i] end)
          if not notBlocked or not notBlocked_ then
            isMoving = false
            break
          end
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] - i][player[2] + i] = nil
        end
     
        -- change player position
        player[1] = player[1] - distance
        player[2] = player[2] + distance
       
        -- increase score
        score = score + distance
       
        isMoving = false
      end
     
    elseif char == '2' and board[player[1]][player[2] + 1] then
      notBlocked, notBlocked_ = pcall(function() return board[player[1]][player[2] + i] end)
      if notBlocked or notBlocked_ then
        distance = board[player[1]][player[2] + 1]
        destination = {player[1], player[2] + distance}

        isMoving = true

        -- make sure move is valid
        for i = 1, distance do
          notBlocked, notBlocked_ = pcall(function() return board[player[1]][player[2] + i] end)
          if not notBlocked or not notBlocked_ then
            isMoving = false
            break
          end
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1]][player[2] + i] = nil
        end
     
        -- change player position
        player[2] = player[2] + distance

        -- increase score
        score = score + distance

        isMoving = false
      end

    elseif char == '3' and board[player[1] + 1][player[2] + 1] then
      distance = board[player[1] + 1][player[2] + 1]
      destination = {player[1] + distance, player[2] + distance}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1] + i][player[2] + i] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] + i][player[2] + i] = nil
        end

        -- move player
        player[1] = player[1] + distance
        player[2] = player[2] + distance

        -- increase score
        score = score + distance

        isMoving = false
      end

    elseif char == '6' and board[player[1] + 1][player[2]] then
      distance = board[player[1] + 1][player[2]]
      destination = {player[1] + distance, player[2]}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1] + i][player[2]] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] + i][player[2]] = nil
        end

        -- move player
        player[1] = player[1] + distance

        -- increase score
        score = score + distance

        isMoving = false
      end

    elseif char == '9' and board[player[1] + 1][player[2] - 1] then
      distance = board[player[1] + 1][player[2] - 1]
      destination = {player[1] + distance, player[2] - distance}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1] + i][player[2] - i] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] + i][player[2] - i] = nil
        end

        -- move player
        player[1] = player[1] + distance
        player[2] = player[2] - distance

        -- increase score
        score = score + distance

        isMoving = false
      end

    elseif char == '8' and board[player[1]][player[2] - 1] then
      distance = board[player[1]][player[2] - 1]
      destination = {player[1], player[2] - distance}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1]][player[2] - i] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1]][player[2] - i] = nil
        end

        --move player
        player[2] = player[2] - distance

        -- increase score
        score = score + distance

        isMoving = false
      end
     
    elseif char == '7' and board[player[1] - 1][player[2] - 1] then
      distance = board[player[1] - 1][player[2] - 1]
      destination = {player[1] - distance, player[2] - distance}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1] - i][player[2] - i] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] - i][player[2] - i] = nil
        end

        -- move player
        player[1] = player[1] - distance
        player[2] = player[2] - distance

        -- increase score
        score = score + distance

        isMoving = false
      end

    elseif char == '4' and board[player[1] - 1][player[2]] then
      distance = board[player[1] - 1][player[2]]
      destination = {player[1] - distance, player[2]}

      isMoving = true

      -- make sure move is valid
      for i = 1, distance do
        notBlocked, notBlocked_ = pcall(function() return board[player[1] - i][player[2]] end)
        if not notBlocked or not notBlocked_ then
          isMoving = false
          break
        end
      end

      if isMoving then
        -- clear spots
        for i = 1, distance do
          board[player[1] - i][player[2]] = nil
        end

        -- move player
        player[1] = player[1] - distance

        -- increase score
        score = score + distance

        isMoving = false
      end
    end
    var.store('score', score)
  end
 
  if char == '5' then
    reset()
  end
 
  platform.window:invalidate()
end

function on.paint(gc)
  -- render the board
  for x = 0, wincharWidth - 1 do
    for y = 0, wincharHeight - 1 do
      if board[x + 1][y + 1] then
        -- number appearance
        gc:setColorRGB(colors[board[x + 1][y + 1]][1])
        gc:setFont("sansserif", colors[board[x + 1][y + 1]][2], 10)
     
        -- draw number
        gc:drawString(board[x + 1][y + 1], x * strWidth, y * strHeight)
      end
    end
  end

  -- draw the player
  gc:setColorRGB(0xFFFFFF)
  gc:setFont("sansserif", 'r', 10)
  gc:drawString('P', (player[1] * strWidth) - strWidth, (player[2] * strHeight) - strHeight)
end


TL;DR if I move off the screen on the X axis, the program crashes. If I move of the screen on the Y axis, the program is fine.
  • Calculators owned: TI Nspire CX, HP Prime
  • Consoles, mobile devices and vintage computers owned: NES

Adriweb

Well, I don't have time to go into specifics to actually give you some code, but do NOT use pcall for things like this. Really.
I mean, it's much worse than a bunch of ifs.

And well, what you need to do is just bound checking, AFAICS. It's clearly an easily solved problem when you think about it for a bit.

What I'd recommend doing is to store in some temporary variable the next position of what the player *would* be. Then, if it's an allowed value, use is as an index for getting the table element. So you won't get errors due to nil values.
So, instead of directly having unchecked read/writes of/to "board[player[1] - i][player[2] + i]", look at what player[1]-i is, same for +i.

Also, you should hugely refactor the on.charIn to do the least amount of code duplication possible. It should be doable at great length, considering the code look similar in many situations.

Let me know how that goes, and I'll come back later
  • Calculators owned: TI-Nspire CX CAS, TI-Nspire CX, TI-Nspire CAS (x3), TI-Nspire (x2), TI-Nspire CM-C CAS, TI-Nspire CAS+, TI-80, TI-82 Stats.fr, TI-82 Plus, TI-83 Plus, TI-83 Plus.fr USB, TI-84+, TI-84+ Pocket SE, TI-84+ C Silver Edition, TI-84 Plus CE, TI-89 Titanium, TI-86, TI-Voyage 200, TI-Collège Plus, TI-Collège Plus Solaire, 3 HP, some Casios
Co-founder & co-administrator of TI-Planet and Inspired-Lua

Strontium

I tried many different ways to avoid using pcall. The first solution, and really the only solution I could think of, involved using metatables. This somewhat worked. For example, I was able to have table[invalidindex] return whatever I wanted, but table[invalidindex][otherinvalidindex] would always error.

As for fixing code duplication, I *just* came up with a method of doing that.

I think the best way of fixing my problems is just to rewrite the game, since at this point, the code is spaghetti.
  • Calculators owned: TI Nspire CX, HP Prime
  • Consoles, mobile devices and vintage computers owned: NES

Powered by EzPortal