Skip to content
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

Done #1448

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Done #1448

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bin/tictactoe
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#!/usr/bin/env ruby

require_relative '../config/environment'


# Greet the user with a message
# Prompt the user for what kind of game they want to play 0,1,or 2 player.
# Ask the user of for who should go first and be "X"
# Use the input to correctly Initialize a Game with the appropriate player types and token values
# When the game is over, the CLI should prompt the user if they would like to play again and allow them to choose a new configuration for the game as described above. If the user dosen't want to play again, exit the program.
68 changes: 68 additions & 0 deletions lib/board.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@

# token method - set it equal to the token of the player object



#Board class represents the data and logic of a Tic-tac-toe game Board
class Board
#Propert CELL - Stores Data of the state of the board in an Array
attr_accessor :cells

@cells = []

#When board is initializes it should with cells from a new game - USE #reset!
def initialize
reset!
end

#Reset! Method - can reset the state of the cells to what a board should look like at the start of the of the game an array with 9 " " elements
def reset!
self.cells = Array.new(9, " ")
end

#display method - A board can print its current state
def display
puts " #{self.cells[0]} | #{self.cells[1]} | #{self.cells[2]} "
puts "-----------"
puts " #{self.cells[3]} | #{self.cells[4]} | #{self.cells[5]} "
puts "-----------"
puts " #{self.cells[6]} | #{self.cells[7]} | #{self.cells[8]} "
end

# position method takes in the user's input in the form of 1-9 strings like "2" or "9" and looks up the value of the cells at the correct index from the array's perspective. All other methods will take input in the user's perspective of 1-9 strings and use #position
def position(user_input)
return self.cells[user_input.to_i - 1]
end


# The board can return values based on its state suh as full? when enrtirely occupied with "X" and "O"
def full?
self.cells.all? {|cell| cell == "X" || cell == "O"}
end

# Turn_count can return values based on how many positions in the cell array are filled.
def turn_count
self.cells.count("X") + self.cells.count("O")
end

#taken? will return true or false for an individual position.
def taken?(position)
#board position -1 because of test using range 1-9 (user input numbers)
self.cells[position.to_i-1] == "X" ||
self.cells[position.to_i-1] == "O"
end

#valid_move? will ensure that moves are between 1-9 and not taken
def valid_move?(position)
!taken?(position) && position.to_i >0 && position.to_i <=9
end

# Update method represents updating the board when player makes a move. Takes two arguments 1st - The position a user wants to occupy in the form of 1-9 strings that will be converted to the board cell's array index and 2nd the player object making the move.
def update(position, player)
self.cells[position.to_i-1] = player.token
end

end



91 changes: 91 additions & 0 deletions lib/game.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# The Game class is the main model of the application and represents singular instance of a Tic-tac-toe session
class Game
# A game has one board through it board property
# A game has two players stored in a player_1 and player_2 property
attr_accessor :board, :player_1, :player_2

WIN_COMBINATIONS = [[0,1,2],
[3,4,5],
[6,7,8],
[0,3,6],
[1,4,7],
[2,5,8],
[0,4,8],
[2,4,6]]

#Board and Player do not directly relate to the Game but do collaborate with each other through arguments

def initialize(player_1 = Players::Human.new("X") , player_2 = Players::Human.new("O"), board = Board.new)
@player_1 = player_1
@player_2 = player_2
@board = board
@board.display
end

def current_player
board.turn_count.odd? ? player_2 : player_1
end

def won?
WIN_COMBINATIONS.each do |combination| #[0,1,2] which is board range (0-8)
if @board.cells[combination[0]] == @board.cells[combination[1]] &&
@board.cells[combination[1]] == @board.cells[combination[2]] &&
@board.taken?(combination[0]+1)
#Need to +1, because #taken? rspec test is working off user_input range (1-9)
return combination
end
end
return false
end

def draw?
@board.full? && !won? ? true : false
end

def over?
(won? || draw?) ? true : false
# IF board is not full, game is in progress (FALSE), ELSE, game is over (TRUE)
end

def winner
if won?
combination = won?
@board.cells[combination[0]] # X or O
end
end

def turn
puts "Please enter a number 1-9:"
@user_input = current_player.move(@board)
if @board.valid_move?(@user_input)
@board.update(@user_input, current_player)
else puts "Please enter a number 1-9:"
@board.display
turn
end
@board.display
end

def play
turn until over?
if won?
puts "Congratulations #{winner}!"
elsif draw?
puts "Cat's Game!"
end
end

end







# Beyond providing relationships with player and a board, the Game instance must also provide the basic game runtime and logic.

# These methods relate to the state of the game such as #current_player, #won and #winner.

# The other methods relate to managing a game like #start, #play and #turn.

15 changes: 15 additions & 0 deletions lib/player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This is root class and nor a valid player of Tic-tac-toe that act as an inheritance point for actual player classes such as Human<Player and Computer<Player

# The Player root class will define only the most basic properties of a player, that they have a token property that can be set upon initialization

#Every player subclass will implement a #move method that represents how that type of player makes a move in Tic-tac-toe

#

class Player
attr_reader :token

def initialize(token)
@token = token
end
end
31 changes: 31 additions & 0 deletions lib/players/computer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Define a class Players::Computer that represents a computer player of Tic-tac-toe

# Implement a move method that acceots a board and returns the move the comouter wants to make in the form of a 1-9 string.

module Players
class Computer < Player
def move(board)
if board.cells[4] == " "
"5"
elsif board.cells[0] == " "
"1"
elsif board.cells[2] == " "
"3"
elsif board.cells[6] == " "
"7"
elsif board.cells[8] == " "
"9"
elsif board.cells[1] == " "
"2"
elsif board.cells[3] == " "
"4"
elsif board.cells[5] == " "
"6"
elsif board.cells[7] == " "
"8"
end
end
end
end


16 changes: 16 additions & 0 deletions lib/players/human.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Define a Human class that inherits from Player(player.rb)
# This class should be namespaced in the module Players because the human.rb file is inside the player/directory

# The human player must implement a #move method that takes in a board argument and allows a human player to enter a move via the CLI. The method should return the value the user enters.. Eveb though the method accepts a board argument, it does not need to use it.

module Players
class Human < Player
def move(board)
input = gets.strip
end
end
end