Giter Club home page Giter Club logo

ttt-8-turn's Introduction

Building a Tic Tac Toe Turn

Objectives

  1. Build a method composed of the use of many methods ("helper methods") previously defined.
  2. Use method return values to control the logic of a composed method.
  3. Use an input validation loop or recursion to create a loop.
  4. Build a CLI that uses a single method call to execute.

Overview

You're going to be building a CLI for a single turn of Tic Tac Toe.

A turn of Tic Tac Toe is composed of the following routine:

  1. Asking the user for their move by position 1-9.
  2. Receiving the user input.
  3. Convert position to an index.
  4. If the move is valid, make the move and display the board to the user.
  5. If the move is invalid, ask for a new move until a valid move is received.

All these procedures will be wrapped into our #turn method. However, the majority of the logic for these procedures will be defined and encapsulated in individual methods (some of which you may have built previously).

You can imagine the pseudocode for the #turn method:

ask for input
get input
convert input to index
if index is valid
  make the move for index
  show the board
else
  ask for input again until you get a valid input
end

Instructions

Helper Methods in lib/turn.rb

Before defining #turn, you should define the following methods:

#display_board

Should accept a board as an argument and print out the current state of the board for the user.

#valid_move?

Should accept a board and an index from the user and return true if the index is within the correct range of 0-8 and is currently unoccupied by an X or O token.

Hint: While not explicitly required by this lab, you might want to encapsulate the logic to check if a position is occupied in its own method, perhaps #position_taken?

#move

This method should accept a board, an index from the user (which was converted from their raw input with input_to_index), and a token to mark that position with (you can give that argument a default value of 'X'––we're not worrying about whose turn it is yet). The method should set the correct index value of that position within the board equal to the token.

Workflow

Start with building those methods (or copying code you might have written before) and making the first few tests in spec/turn_spec.rb pass. You can use the learn --fail-fast or rspec --fail-fast mode to only see 1 failure at a time and allow you to work through those method definitions.

You'll then need to build your #turn method. Before building a full #turn method according to the failing tests, let's setup a quick CLI so that you can watch your #turn method perform as you build, visually confirming it behaves as expected.

Open bin/turn, you'll see that it is already setup with #!/usr/bin/env ruby so you can execute it by running ./bin/turn or ruby bin/turn from your terminal. It currently does nothing (because it has no code), but try it out just for fun.

The purpose of this file is to execute a turn of Tic Tac Toe. The first thing it needs to do is load our library of methods defined in lib/turn.rb.

Edit bin/turn:

#!/usr/bin/env ruby
require_relative '../lib/turn'

By adding require_relative '../lib/turn' we are telling Ruby to load a file from a relative path to the current file. Since we're in bin we have to go up a directory and into lib to find turn.rb, thus the path ../lib/turn. You never need to give the .rb extension to a path for require_relative. Ruby assumes you mean a .rb file.

Next, the CLI needs to setup the data required to play a game of Tic Tac Toe, namely, the board variable to store the array we use to keep track of the state of the board.

Edit bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

We're now ready to start the game. Let's welcome the user and show them the board at the start of the game.

Edit bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

puts "Welcome to Tic Tac Toe!"
display_board(board)

Notice how we evoke the #display_board method defined in lib/turn.rb, passing the local board data into it via an argument on the last line.

Now let's run this CLI: bin/turn or ruby bin/turn from your terminal. You should see:

$ ./bin/turn
Welcome to Tic Tac Toe!
   |   |   
-----------
   |   |   
-----------
   |   |   

Great! Now the next thing the CLI needs to do is kick off a turn of the game. We know we're going to build a #turn method to encapsulate that procedure, so even though we haven't defined it yet, let's add the call to the soon-to-be-coded #turn method to our CLI right now.

Edit bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

puts "Welcome to Tic Tac Toe!"
display_board(board)
turn(board)

If we ran the CLI right now, without defining #turn in lib/turn.rb, we'd get a NameError complaining about an undefined local variable or method turn.

Let's quickly jump to lib/turn.rb and just stub out the most simple version of the #turn method.

Add to lib/turn.rb

def turn(board)
  puts "Please enter 1-9:"
end

Let's run the CLI now: bin/turn or ruby bin/turn from your terminal. You should see:

$ ./bin/turn
Welcome to Tic Tac Toe!
   |   |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:

Great! Now as we add logic to #turn, we can use our CLI to see how it behaves.

#turn

The hard part of the #turn method is figuring out how to handle invalid input. We know that when a user enters invalid input, we want to ask them for input again. Imagine the pseudocode again:

get input
convert input to index
if index is valid
  make the move for input
else
  ask for input again until you get a valid input
end

Asking for input again is the hard part. We either need a mechanism to repeat the entire logic again until input satisfies the valid requirement, like a loop of some sort, or we need to be able to execute a procedure that asks for a user's input again. It's almost like what we might want to do in the event of invalid user input is just replay the entire turn. No move was made, so why not just run #turn again?

Calling a method from within itself is totally okay in programming. In fact, it is an elegant solution to some complex problems. Recursion is the repeated application of the same procedure. Google it! There's an easter egg from Google developers on that page; can you find it?

As you are already familiar with loops, that is a totally acceptable solution to the input validation problem as well.

As you try to get it working, keep playing with bin/turn until it works as expected, endlessly asking you for a valid turn input. If you ever need to exit the CLI without giving an input, just hit CTRL+C (sometimes ALT+C or COMMAND+C).

The tests will pass once you have your CLI working right, but don't be scared of running the tests to see the failures for #turn and seeing if they point you in the right direction.

Conclusion

Once you define #turn as specified by the tests in spec/turn_spec.rb, your CLI should work for a turn of Tic Tac Toe and running it should yield:

$ ./bin/turn
Welcome to Tic Tac Toe!
   |   |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
1 # I entered 1 in response to the gets prompt.
 X |   |   
-----------
   |   |   
-----------
   |   |   

A subsequent run might yield:

$ ./bin/turn
Welcome to Tic Tac Toe!
   |   |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
5
   |   |   
-----------
   | X |   
-----------
   |   |   

Currently our program only allows us to run 1 turn, the first turn. If you wanted to see how #turn would behave say on the third turn of the game, make the following edits to bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

puts "Welcome to Tic Tac Toe!"

move(board, 4, "X")
move(board, 0, "O")

display_board(board)

turn(board)

Notice that before the game even really starts, we hard code an execution of two moves, X to the middle position (4), and O to the top left (0).

When we run the CLI, we'd see:

$ ./bin/turn
Welcome to Tic Tac Toe!
O |   |   
-----------
  | X |   
-----------
  |   |   
Please enter 1-9:

The board is pre-filled and the turn will now add a 3rd token to the board.

Try this edit to bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

puts "Welcome to Tic Tac Toe!"

move(board, 4, "X")
move(board, 0, "O")
move(board, 1, "X")

display_board(board)

turn(board)

Here we are manually making 3 moves, an X, an O, and then an X, as would proceed normally in a real game. That means that the next turn belongs to the O player, right? Run your CLI and enter a move for O and see what happens. Can you guess?

$ ./bin/turn
Welcome to Tic Tac Toe!
 O | X |   
-----------
   | X |   
-----------
   |   |   
Please enter 1-9:
8
 O | X |   
-----------
   | X |   
-----------
   | X |   

It was O's move and when I entered 8 to block X with my O, our program put an X in!!! Why? We'll have to fix that. Can you anticipate what we might need to build to make that work?

The other issue with our current program is that only one turn is being played. We could actually work around that right now. How many turns does our CLI execute? Currently only 1 (even with the explicit calls to move, those aren't user turns, they are only updating the board). Is there anything preventing us from just calling #turn 9 times so it's like a real game with 9 turns? Nope! Let's try it with a final edit to bin/turn:

#!/usr/bin/env ruby

require_relative '../lib/turn'

board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]

puts "Welcome to Tic Tac Toe!"

display_board(board)

turn(board)
turn(board)
turn(board)
turn(board)
turn(board)
turn(board)
turn(board)
turn(board)
turn(board)

Here's an entire execution of this CLI (remember, when you see a number that means the player put in that input).

./bin/turn
Welcome to Tic Tac Toe!
   |   |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
1
 X |   |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
2
 X | X |   
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
3
 X | X | X
-----------
   |   |   
-----------
   |   |   
Please enter 1-9:
4
 X | X | X
-----------
 X |   |   
-----------
   |   |   
Please enter 1-9:
5
 X | X | X
-----------
 X | X |   
-----------
   |   |   
Please enter 1-9:
6
 X | X | X
-----------
 X | X | X
-----------
   |   |   
Please enter 1-9:
7
 X | X | X
-----------
 X | X | X
-----------
 X |   |   
Please enter 1-9:
8
 X | X | X
-----------
 X | X | X
-----------
 X | X |   
Please enter 1-9:
9
 X | X | X
-----------
 X | X | X
-----------
 X | X | X

Another issue, besides only marking Xs as described above, is that the game played way too many turns! We need it to know how to quit if someone wins.

Even with these deficiencies, this #turn method means you are very close to building a complete Tic Tac Toe game. Get excited!

View Building a Tic Tac Toe Turn on Learn.co and start learning to code for free.

ttt-8-turn's People

Contributors

annjohn avatar aturkewi avatar aviflombaum avatar gj avatar jonbf avatar maxwellbenton avatar mendelb avatar milothiesen avatar neudork avatar peterbell avatar sammarcus avatar shmuwol avatar sophiedebenedetto avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ttt-8-turn's Issues

Error trying to test/submit code (all tests passing)

ttt-8 error

When I type "learn" to test my program, I get the above results and then it seems to freeze (or be doing something in the background) for 30 seconds to a minute. Then I can finally type again in the terminal. It seems like all the tests are passing so I"m not sure why I can't submit and move on to the next lesson.

false positive?

def turn(board)
puts "Please enter 1-9:"
input = gets.strip
move(board, input)
display_board(board)
end

I was able to get all tests to pass with this code.

This repeatedly asks for an input until a valid input is given as desired, but after entering a valid input it calls #display_board as many times as it called #turn printing multiple boards. So if user inputs "a", then "b", then "2", it will ask the user to enter 1-9 until the "2" is entered, but then it will display the new board 3 times.

I changed my code to this to get it working right:

def turn(board)
puts "Please enter 1-9:"
input = gets.strip
if move(board, input)
display_board(board)
end
end

#turn validates input correctly test

This test seems to be written incorrectly. You're passing it a test as if the #valid_move method is a stand alone method. Its arguments are being used and changed elsewhere, and my other tests can't pass if you're passing it a string.

Lastly, after looking at Learn's solution, would those methods have passed in the previous labs?? It seems like it isn't encouraged to build upon previous labs.

def display_board(board)
puts " #{board[0]} | #{board[1]} | #{board[2]} "
puts "-----------"
puts " #{board[3]} | #{board[4]} | #{board[5]} "
puts "-----------"
puts " #{board[6]} | #{board[7]} | #{board[8]} "
end

def turn(board)
puts "Please enter 1-9:"
position = gets.strip
position = position.to_i

if(valid_move?(board, position))
move(board, position, "X")
display_board(board)
else
turn(board)
end
end

def valid_move?(board, position)
position = position.to_i
position = position -= 1

if position.between?(0, 8) != true
return false
elsif board[position] == " " || board[position] == "" || board[position] == nil
return true
else
return false
end

end

def move(board, position, token)
position -= 1
board[position] = token
end

Test driving the solution is confusing with turn_spec:'asks the user for input by printing: "Please enter 1-9:"'

When I add the necessary puts, I now get an error "NoMethodError:undefined method `turn' for" but the spec as displayed to the student doesn't say it's testing the ability to call turn.

I think we might want to remove turn_spec:63 " turn(board)" so the spec description more honestly and precisely reflects the thing that is being tested.

Not submitting a PR as I'm really new here and could be way off base, but if I get a 👍 on the sentiment, I can (a) submit a PR to change this and (b) go back through the intro to ruby track being super picky about ensuring we test exactly one thing at a time with each tests. it'd do me the world of good (I'm rusty at this programming stuff!) and it just might reduce some accidental complexity for students.

Inputs appreciated!

Question

Im trying to ask a question on learn. Im not receiving the option to input my inquiry.

Tic Tac Toe Turn missing a file

Link to Canvas

https://learning.flatironschool.com/courses/4537/assignments/126690?module_item_id=262344

What should be changed?

The lesson says the repository should have a lib/play.rb that comes with helper methods from previous exercises. However, lib/play.rb does not exist in the repository. lib only contains turn.rb, which is blank (since that's where the user is supposed to code).

Additional context

README.md in the repository has different text from the lesson, and doesn't claim the existence of lib/play.rb

ttt-8 rspec issue?

see my question on this - code works fine if i run it normally, checking valid input, checking valid board, asking again if bad user input, etc . but 'learn' hangs and gets a stack trace fault because of something wrong with rspec script i suspect. i use recursion for solution of asking again for user input. others are seeing similar issues i believe. thanks
screen shot 2016-05-21 at 11 24 58 pm

Needs to be reviewed

👀 @SophieDeBenedetto

Due 9/11/15 EOD

Feel free to make edits in master / solution directly, just make sure to ping me the commits so I can see the diffs. It'd be great, if you are going to make edits in master / solution, especially if they are multiple commits, if you squashed them locally first before pushing so that I can look at 1 single diff and easily revert or just update with new commits.

Recursion & loops too advanced at this point?

It's my sense that recursion is too advanced a concept for the beginner programmer to grasp and use effectively this early on. I feel like they are ready for loops, which are about to come next, but since not covered it's tough to ask them to use either recursion or loops to solve this.

Feels like this lab should come after basic loops have been covered and we should instruct user to use loops, avoid talk of recursion.

feels out of order?

it tells you to pass the tests, and then after that goes into an explanation of how we might go about solving this lab.

Tic Tac Toe Turn -Turn method not working properly

def turn(board) puts "Please enter 1-9:" user_input = gets.strip index = input_to_index(user_input) if valid_move?(board, index) move(board, index, player) display_board else turn(board) end end

I tried re-implementing this method many times with different approaches and I don't know why my valid_move method is throwing all the test cases off. Any idea why it's not working?

Can't run local test but the work is submitted

// ♥ learn test
./lib/turn.rb
#display_board
prints arbitrary arrangements of the board
#input_to_index
converts a user_input to an integer
subtracts 1 from the user_input
returns -1 for strings without integers
#valid_move?
returns true/false based on index
#move
allows "X" player in the bottom right and "O" in the top left
#turn
asks the user for input by printing: "Please enter 1-9:"
gets the user input
calls the input_to_index method

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.