0

I'm creating a game of tic-tac-toe. I am new to ruby, and have defined a class Game, with two inner-classes Player and GameBoard. Within Game I define two instance-variables player1 and player2. I am trying to access both player1 and player2 inside of the method GameBoard.update_gameboard like so

def update_gameboard(player)
      move = if player == 1
               @player1.moves[@player1.turn]
             else
               @player2.moves[@player2.turn]
             end
.
.
.

And I get the error:

/Users/Jacob/Development/RubyDev/RubymineProjects/TicTacToe/tictactoe.rb:85:in `update_gameboard': undefined method `moves' for nil:NilClass (NoMethodError)
    from /Users/Jacob/Development/RubyDev/RubymineProjects/TicTacToe/tictactoe.rb:44:in `play'
    from /Users/Jacob/Development/RubyDev/RubymineProjects/TicTacTOe/main.rb:30:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

Process finished with exit code 1

moves is not a method, it's an array. (play is a method in Game which calls update_gameboard)

How do I access the instance-variables of an outer-class (Game) from an inner class (e.g. GameBoard)?

class Game
  attr_accessor :winner, :player1, :player2, :gameboard

  def initialize(player1_name, player2_name)
    @player1 = Player.new(player1_name, 'X')
    @player2 = Player.new(player2_name, 'O')
    @gameboard = GameBoard.new
  end
.
.
.
  class GameBoard
    attr_accessor :current_gameboard
    attr_reader :gameboard_move_map

    def initialize
          # initialize irrelevant GameBoard variables
    end

    def update_gameboard(player)
      move = if player == 1
               @player1.moves[@player1.turn]
             else
               @player2.moves[@player2.turn]
             end
      #then some more stuff that uses @player1 and @player2
    end
.
.
.
  end
end



solidstatejake
  • 202
  • 3
  • 9
  • Possible duplicate of [Access outer class from inner class in python](https://stackoverflow.com/questions/2024566/access-outer-class-from-inner-class-in-python) – Ishaan Javali Jan 12 '19 at 23:27
  • @IshaanJavali I saw that question. With respect to this situation, it did not help me. – solidstatejake Jan 13 '19 at 03:51
  • That could possibly be because I'm not educated enough in OOP to understand how to apply those instances to my work in ruby. – solidstatejake Jan 13 '19 at 04:10

1 Answers1

4

Really important concept here. "Inner class" does not do what you think it is. There is absolutely no relationship or special behavior between the two classes, other than namespacing. Basically, just treat the inner class like it is defined totally separately, because that's how it behaves.

One thing you can do, though, is pass the GameBoard constructor a reference to the Game instance, so you can access the instance variables:

(note that i am only showing the parts relevant for the answer, and am ommitting the other bits of your code for the sake of being succinct):

class GameBoard
  attr_reader :game
  def initialize(game)
    @game = game
  end
end

class Game
  def initialize(player1_name, player2_name)
    @player1 = Player.new(player1_name, 'X')
    @player2 = Player.new(player2_name, 'O')

    # Note I pass the Game instance as self here:
    @gameboard = GameBoard.new(self)
  end
end

Then inside the GameBoard instance methods, you can do something like this:

game.player1.moves[game.player1.turn]

game here is the method generated by attr_reader (referring to the @game instance variable set in initialize).

max pleaner
  • 26,189
  • 9
  • 66
  • 118