The Odin Project Blog Post 12
March 31 2017
Week 12 - Deeper Into Git and Beginning Rails
This week I decided to wrap up the Ruby Programming section of TOP, work on some things I’ve been meaning to do for my portfolio, and begin the Rails section. The last few weeks have been project-laden and thought-intensive, so this week was a good chance for me to take the iron out of the fire for a bit. That doesn’t mean I didn’t get anything done, however. The end of the Ruby section has a number of readings on Git, that delve deeper into the tool than just the add/commit/push commands that I’ve been using up to this point. I now have a more complete mental map of what is happening between my files and repositories. I also got around to writing a few Readme’s for projects that didn’t already have them in my GitHub - a key component for showing off your code. Finally, I began the much anticipated Rails section, albeit just a tiny portion. Already, it has piqued my interest so much that I’m itching to finish this post so that I can get back to that.
There is a final project in the Ruby section that asks us to build a full-fledged chess board and game that I decided to skip. Thinking about the amount of time this would take me, I figured my time would be better spent on other things: such as sharpening my GitHub (the readmes I worked on), and moving onto the Rails section. I’m fairly confident in my Ruby abilities, and don’t think the chess project would give me any better advantage moving forward. Rails projects, I’m led to believe, is where I’m going to benefit the most in a personal project and/or on a portfolio. Furthermore, I think the whole point of the TOP’s Ruby section was to get familiar enough with Ruby so that we could understand the “Rails magic”. It was not necessarily to build elaborate Ruby projects. So for now, I am moving on.
March 30 2017
- Wrote Data Structures readme
- Wrote Ruby Building Blocks readme
- Began looking over TOP's Getting Hired section
- Began Rails section of TOP curriculum - web guesser app tutorial
March 29 2017
- Completed think-like-a-git
- Wrote a Readme for Connect Four
- SaaS video around 31:00 talks about merge conflicts
March 28 2017
March 27 2017
- update weekly goals
- Git lesson on TOP
The Odin Project Blog Post 11
March 24 2017
Week 11 - TDD or Not TDD & Connect Four
As it turns out, my woes with testing my tic tac toe game from last week were (somewhat) unfounded. After spending hours trying to figure out what I was doing wrong, I asked Stack Overflow and quickly got a response that my code was working on someone else’s machine, and it was likely that I was testing a file saved on the wrong path. In the end, all it took was me re-saving my original game file into the new directory with the spec file I had created. Though extremely frustrating for all the time I wasted, it was reassuring that I was writing the Rspec correctly. Here’s an abbreviated version of the game and spec file:
class Board
attr_reader :board, :current_turn, :game_over
def initialize
@board = [
0, 1, 2,
3, 4, 5,
6, 7, 8
]
@current_turn = "X"
@game_over = false
end
def show_board
print @board[0..2]
print "\n"
print @board[3..5]
print "\n"
print @board[6..8]
end
def change_turn
@current_turn = @current_turn == "X" ? "O" : "X" unless @game_over == true
end
def player_move(index)
if @board[index] == "X" || @board[index] == "O"
puts "That space is taken"
@current_turn
else
@board[index] = @current_turn
show_board
check_for_winner
change_turn
end
end
private
def check_for_winner
if @board[0] == @board[4] && @board[4] == @board[8]
@game_over = true
puts "Three in a row! #{@current_turn} has won!”
require "tictactoe"
describe Board do
before(:all) do
@game = Board.new
end
describe ".player_move" do
it "sets the X or O at the proper place on the board" do
@game.player_move(8)
expect(@game.board[8]). to eq("X")
end
it "changes turn" do
expect(@game.current_turn).to eql("O")
end
it "sets game over to true when X or O are in 3 consecutive spots" do
@game.player_move(0)
@game.player_move(5)
@game.player_move(1)
@game.player_move(6)
@game.player_move(2)
expect(@game.game_over).to be true
end
end
end
Before all tests, the first thing I do is create a new Board object called @game. This will be used as my test subject. Because of the way I set up my game, everything runs through the player_move method. Therefore I'm only testing that function. I run three tests on it. The first one is that it sets the X or O to its proper place on my board. I do this by literally making a move with @game.player_move(8), and then expecting the outcome at that space on the board to equal "X". Next, I expect that it changes turn to "O". Finally, I make sure that three in a row sets my game_over instance variable to true which ends the game.
We were supposed to take this lesson and apply it to a connect four style game but in reverse order. So in real test driven development you write the tests first and then write the code to make the tests pass. I began my game this way, but soon became frustrated, just wanted the code to work, neglected the tests, and now the game is fully functional and none of my tests work. Bright side: I built a connect four game, and it was really hard, and it was really satisfying. Down side: I didn't do it TDD style, but I think I'll keep working on it until I figure out what I'm doing wrong. Anyway, here's the game:
class Game
attr_reader :current_turn, :board, :game_over
def initialize
@current_turn = "R"
@board = [
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(1 2 3 4 5 6 7)]
@game_over = false
show_board
play
end
def change_turn
@current_turn = @current_turn == "R" ? "Y" : "R"
end
def play
until @game_over == true
place_piece
check_for_winner
change_turn
show_board
end
end
def place_piece
puts "pick a column to drop your piece #{current_turn}"
column = gets.chomp.to_i
check_move?(column)
end
def show_board
@board.each do |row|
puts ' ' + row.join(' ')
end
end
def check_move?(move)
if (move.between?(1, 7) && @board[0][move-1] == "_")
true
index = 6
while @board[index][move-1] != "_"
index -=1
end
@board[index][move-1] = @current_turn
else
puts "Invalid move, try again"
!change_turn
end
end
def check_for_winner
(0..5).each do |x|
(0..6).each do |y|
# horizontal
if @board[x][y] == @current_turn &&
@board[x][y+1] == @current_turn &&
@board[x][y+2] == @current_turn &&
@board[x][y+3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
# vertical
if @board[x][y] == @current_turn &&
@board[x+1][y] == @current_turn &&
@board[x+2][y] == @current_turn &&
@board[x+3][y] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
# diagonal left
if @board[x][y] == @current_turn &&
@board[x+1][y-1] == @current_turn &&
@board[x+2][y-2] == @current_turn &&
@board[x+3][y-3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
#diagonal right
if @board[x][y] == @current_turn &&
@board[x+1][y+1] == @current_turn &&
@board[x+2][y+2] == @current_turn &&
@board[x+3][y+3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
end
end
end
end
TOP wanted us to practice TDD with this project, claiming this game "wouldn't take too much brain power". Preposterous. I spent three, long days stewing over this program, and I think it took the most ingenuity of any project we've put together so far. The basic structure is the same as my tic tac toe game in that it has a central method that the other methods get called from. The game_over instance variable that signfies the end of the game is also the same. However, this time the board is made up of a two-dimensional array, 42 places in all, and the user input is handled a little differently.
@board = [
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(_ _ _ _ _ _ _),
%w(1 2 3 4 5 6 7)]
The bottom array of the board is made up of an array of numbers. In the place_piece method, the user is asked to choose a column to place his/her piece. Once the program receives this number it sends that info to the check_move? method.
def check_move?(move)
if (move.between?(1, 7) && @board[0][move-1] == "_")
true
index = 6
while @board[index][move-1] != "_"
index -=1
end
@board[index][move-1] = @current_turn
else
puts "Invalid move, try again"
!change_turn
end
end
If this number is on the board and current place is occupied by "_", either R or Y will take its place. If that place is already taken, either R or Y will try the next place up, until it gets to the top of the column. If the whole column is taken or if the user entered a number not on the board, it will respond with an error message. Though this part was tricky, I never thought I was going to figure out how to solve the check_winner method.
def check_for_winner
(0..5).each do |x|
(0..6).each do |y|
# horizontal
if @board[x][y] == @current_turn &&
@board[x][y+1] == @current_turn &&
@board[x][y+2] == @current_turn &&
@board[x][y+3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
# vertical
if @board[x][y] == @current_turn &&
@board[x+1][y] == @current_turn &&
@board[x+2][y] == @current_turn &&
@board[x+3][y] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
# diagonal left
if @board[x][y] == @current_turn &&
@board[x+1][y-1] == @current_turn &&
@board[x+2][y-2] == @current_turn &&
@board[x+3][y-3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
#diagonal right
if @board[x][y] == @current_turn &&
@board[x+1][y+1] == @current_turn &&
@board[x+2][y+2] == @current_turn &&
@board[x+3][y+3] == @current_turn
puts "#{current_turn} wins!"
@game_over = true
end
end
end
It took me hours of considering to figure out how to loop over my board and then write the loop into my indeces as it's written in the solution. It ended up being pretty simple, which is often the case. To find four in a row, I used an each loop of the columns within an each loop of the rows. Admittedly, I had no idea it was going to work until I tried the horizontal case, which I knew kept the same x-index, with an incrementing y-index. Once that worked, the rest was cake. For a vertical winner, the y stays the same and x increments. For a diagonal left the x increments while the y decrements, and for a diagonal right both x and y increment.
As I mentioned, the tests for this game don't currently work, but I'm happy right now that the game is fully functional. I'll figure out the tests next.
March 22 2017
- Completed Connect Four GitHub but need to work on the testing
March 21 2017
- Worked on Connect Four project
- Updated this GH-pages blog
March 20 2017
The Odin Project Blog Post 10
March 17 2017
Week 10 - Test Driven Development
I’m writing this post as I am still struggling to grasp the concepts and syntax of the Rspec library. The very simple things I have no problem with. If it’s just testing a method or methods within a class, I can get it work. However, I spent the majority of the day yesterday trying to learn how to get my methods within an object to interact with each other within a test and call instance variables that were initialized when the object was created. Little nuances like that are still foreign to me, and I’m especially having trouble finding resources online that teach the basics, which is extremely frustrating. Nonetheless, I will walk through some of the basics of TDD and tests that I have applied to past projects here.
recall the Caesar Cipher
def caesar_cipher(string, num)
cipher = ""
string.each_char do |ch|
ch = ch.ord
if ch.between?(97, 122) || ch.between?(65, 90)
ch += (num % 26)
if ch > 122 || (ch > 90 && ch < 97)
return ch -= 26
end
end
ch = ch.chr
cipher << ch
end
puts cipher
end
The tests I applied to this method look like this
require "Caesar_Cipher"
describe "caesar_cipher" do
it "shifts a letter by a given value" do
expect(caesar_cipher("a", 3)).to eql("d")
end
it "shifts a string by a given value" do
expect(caesar_cipher("aaa", 3)).to eql("ddd")
end
it "leaves uppercase and lowercase intact" do
expect(caesar_cipher("aBcD", 3)).to eql("dEfG")
end
it "leaves special case characters intact" do
expect(caesar_cipher("abc!", 3)).to eql("def!")
end
it "accounts spaces in the string" do
expect(caesar_cipher("What a string!", 3)).to eql("Zkdw d vwulqj!")
end
it "wraps from the end of the alphabet to the beginning" do
expect(caesar_cipher("zzz", 1)).to eql("aaa")
end
end
The object of test driven development is to write a test for a function before actually writing the function, have the test fail, then write the function so that it passes the test. Obviously in these cases I wrote the code first, but I can understand the merit in the process. The above Rspec code tests 6 cases within the caesar cipher function. As you can see the language is very English-like, making it easy to understand what it is you're testing. First I tell the program what the name of the method is I want to test by using the describe key word. Within this describe block, I say what it actually is about the method that I'm going to run and test. Next we plug in our method with paramaters and tell the computer what we expect to get in return. If what we expect in Rspec matches what the computer got when we run it in the terminal, the test passes.
module Enumerable
def my_each
i = 0
while i < self.length
yield(self[i])
i +=1
end
self
end
def my_each_with_index
i = 0
while i < self.length
yield(self[i], i)
i += 1
end
self
end
def my_select
select = []
i = 0
while i < self.length
self.my_each do |i|
if yield i
select.push(i)
i += 1
end
end
select
end
Here's an example with a tiny bit more complexity, but not much.
require "Enumerables"
describe Enumerable do
let(:array) { [1,2,3,4] }
describe "my_each" do
it "returns this array" do
expect(array.my_each{ |num| num}).to eql(array)
end
end
describe "select" do
it "returns array with selected values" do
expect(array.select{ |item| item < 3 }).to eql([1,2])
end
end
Unlike the last example, this code is testing a group of methods within a module. The only change that needs to be made in the Rspec file is we nest our describe blocks inside a describe block that singles out the module as a whole. Another difference we can see here is the declaration of an array variable. I used the let key word to declare an array which I then used to test the iterators I created in my module.
I am working on testing my Tic Tac Toe game, but as previously mentioned, am having trouble with the function interactions.
March 15-16 2017
- Began and worked on TDD warmup project
March 14 2017
- Completed knight's travails problem GitHub
- Followed this TDD tutorial to write my first rspec test
require "string_calculator"
describe StringCalculator do
describe ".add" do
context "given an empty string" do
it "returns zero" do
expect(StringCalculator.add("")).to eql(0)
end
end
context "two numbers" do
context "Given '2,4'" do
it "returns 6" do
expect(StringCalculator.add("2,4")).to eql(6)
end
end
context "Given '17,100'" do
it "returns 117" do
expect(StringCalculator.add("17,100")).to eql(117)
end
end
end
end
end
March 13 2017
- Updated weekly goals
- Worked on knight's travails problem in Data Structures and Algorithms project
The Odin Project Blog Post 9
March 10 2017
Week 9 - Data Structures
Unlike the previous two weeks, which gave me trouble despite being familiar from prior independent studies, this week’s concepts introduced a new anxiety. As we are exploring more of the world of computer science, I am having more and more trouble finding what it is we are making the computer do. For example, when I started out, I was mainly studying HTML/CSS and JavaScript. This was all done in the browser, thus I could see the results of what I was doing instantly. Even when TOP switched over to Ruby and I started with the command line and files, I could see that I was taking input from the user, opening files and manipulating their data, and returning information to the user.
This week, however, I lost my way. The lesson was on data structures, and there were three projects(practice problems) with a relatively sparse amount of material on the subject. I think I made the mistake of attempting the projects before fully understanding the topic so I will try to explain it here, in retrospect. A data structure, in the simplest terms, is an organized way to store data for your application. The two types we focused on were linked lists and binary trees. After a lot of hunting, I finally found an explanation that described linked lists in plain English here.
Basically, as I understand it, the main difference between an array and a linked list is that you can add new items to the LL without having to make a new copy of it, unlike an array. This is done with the LL's node and pointer system. Each value in the list sits on a node, and points to the next node, with the final node pointing to nil. If you want to add a new value, you just make the node before it point to it, and have the new node point to the node after it. Here is a visualization of [1, 2, "dog", 365] as a linked list:
(1) -> (2) -> ("dog") -> (365) -> nil
Here is my linked lists project GitHub
Next up was the binary tree. The best way to describe this data structure is for me to compare it to the linked list. While the linked list is a linear structure, meaning each node points to one other item in a single, possibly long line, binary trees' nodes can point to two nodes at a time. This allows for much faster search times. Here's a visual example taken from a Stack Overflow answer
LINKED LIST sorted alphabetically
Alice
/ \
= Bob
/ \
= Chloe
/ \
= David
/ \
= Edwina
/ \
= Frank
/ \
= =
BINARY TREE sorted length-wise from the root
Chloe
___/ \___
Bob Edwina
/ \ / \
Alice = David Frank
/ \ / \ / \
= = = = = =
In the first code example, if you wanted to find Frank, you would have to traverse six nodes to get to him. This is a linked list that is sorted alphabetically from Alice to Frank. In the second example, it would only take you three nodes. This one's a little more complicated. If the name is shorter than the root(Chloe) it gets moved to the left, while if it's longer then it gets moved to the right. Subsequently, if it is shorter or longer than the next node, these same steps are followed until it gets placed. It is added complexity, but you can see the advantage. Here is my binary search project GitHub
And with that I think I've regained my footing in the world of computer science. No one can explain things better than you can to yourself. I have no doubt I'll get lost again next week.
March 8-9 2017
- Completed Binary Search Tree project GitHub
March 7 2017
- Updated CSS on blog (background color, links)
- Began work on Data Structures project
March 6 2017
- Updated weekly goals
- Completed Linked Lists project GitHub
- Worked on blog index page and css
The Odin Project Blog Post 8
March 3 2017
Week 8 - Recursion
Recursion is the programming concept of calling a function from within itself. In effect this creates a loop of endless function calls if there is no condition stated that will end the program at a certain point. It is not extremely common to use recursion (thank God!) but it is very handy to know. Often times an iterator of some sort will suffice, but there are times when recursion will provide a more elegant solution.
There is no better way to explain than to jump into some code…
def factorial(n)
return 1 if n <= 0
n * factorial(n-1)
end
This is a factorial function that uses recursion. If you call factorial(5), within the program it will do 5 * factorial(4). This will then go a level deeper; meaning factorial(4) will get called and the function will run 4 * factorial(3). Again factorial(3) will get called and 3 * factorial(2) will get run. Then 2 * factorial(1) and finally 1 * factorial(0). Because we stated within the function to return 1 if n <= 0, the program does not blow the stack, but returns 1 when it reaches 0. 5*4*3*2*1 is output and we get 120.
def fib(n)
return 0 if n == 0
return 1 if n == 1
fib(n-1) + fib(n-2)
end
Here is a much more complicated program. The Fibonacci sequence is a sequence of numbers starting from 1 that adds the previous 2 numbers to get the next number. For instance, 1+0 is 1 (0, 1, 1), 1+1 is 2 (0, 1, 1, 2), 2+1 is 3 (0, 1, 1, 2, 3) and so on.
In this function, if we input a 5, we get fib(4) + fib(3). This then gets broken down into a two-sided problem. On the left side we'll have fib(3)+fib(2). Again, this gets broken down. Fib(2) leaves us with fib(1)+fib(0) or just 1 and 0. Fib(3) becomes fib(2)+fib(1) but then fib(1) leaves us with 1. Fib(2) breaks down into fib(1)+fib(0) which gives us a 1 and a 0. On the left side of this problem we count up the amount of 1s that are left: which is 3.
Now we can go back to the right side of the original input. fib(3) becomes fib(2)+fib(1). Fib(1) gives us a 1. Fib(2) gives us fib(1)+fib(0) or a 1 and a 0. On the right side we are left with 2 1s. After adding up the left side and the right side, we conclude that the fifth number in the fibonacci sequence is 5.
Like last week’s lesson, this was another section that I studied and struggled with during my JavaScript days prior to TOP. Again, I did not enter the lesson discouraged. Instead, I was actually eager to possibly gain new perspectives on the topic and break through previous limitations I had while attempting to grasp the concept. The material TOP provided, in addition to practice problems, the project, and writing this blog have made me more confident in dealing with recursive methods, though I am still far from an expert.
March 2 2017
- Began work on Linked Lists project
March 1 2017
- Data Structures Wiki entry
- Video on stacks and queues
- CS50x video on binary trees
- Gentle Introduction to Algorithms for Web Developers post
- Mycodeschool videos on