|
| 1 | +--- |
| 2 | +title: It's time for war. Code. War |
| 3 | +published: true |
| 4 | +description: Day 4 of my first round of 100DaysOfCode. Time for practice. |
| 5 | +tags: 100daysofcode, elixir |
| 6 | +cover_image: https://cdn.hashnode.com/res/hashnode/image/upload/v1613441788636/o9mexGvaa.jpeg?auto=compress |
| 7 | +datePosted: '2021-02-15' |
| 8 | +devToId: 605183 |
| 9 | +--- |
| 10 | + |
| 11 | +# Round 1. Day 4 |
| 12 | + |
| 13 | +Now equiped with the knowledge of elixir basics from [ElixirSchool](https://elixirschool.com/en/lessons/basics/basics/), I figure it is time I start practicing, and building my first elixir app. I started of today's practice with a code challenge from [CodeWars](https://www.codewars.com/kata/54edbc7200b811e956000556/train/elixir). The Instructions were as follows: |
| 14 | + |
| 15 | +<br> |
| 16 | + |
| 17 | +> Consider an array/list of sheep where some sheep may be missing from their place. We need a function that counts the number of sheep present in the array (true means present). |
| 18 | +> For example, |
| 19 | +
|
| 20 | +```elixir |
| 21 | +[true, true, true, false, |
| 22 | + true, true, true, true , |
| 23 | + true, false, true, false, |
| 24 | + true, false, false, true , |
| 25 | + true, true, true, true , |
| 26 | + false, false, true, true] |
| 27 | +``` |
| 28 | + |
| 29 | +> The correct answer would be `17`. |
| 30 | +
|
| 31 | +<br> |
| 32 | +The boilerplate code for this challenge was: |
| 33 | + |
| 34 | +```elixir |
| 35 | +defmodule Shepherd do |
| 36 | + def count_sheeps(sheeps) do |
| 37 | + # TODO: for Elixir only true/false values can be presented the in sheeps list |
| 38 | + end |
| 39 | +end |
| 40 | +``` |
| 41 | + |
| 42 | +This challenge didn't seem too hard and it also felt a little familiar. I quickly went through my elixir school notes, and found a recursive counting function that would be a good template for solving this challenge. In the elixir school [functions](https://elixirschool.com/en/lessons/basics/functions/) there was a function that counted the length of an array. |
| 43 | + |
| 44 | +```elixir |
| 45 | +defmodule Length do |
| 46 | + def of([]), do: 0 |
| 47 | + def of([_ | tail]), do: 1 + of(tail) |
| 48 | +end |
| 49 | +``` |
| 50 | + |
| 51 | +If we combine this logic with a little bit of pattern matching it should do the trick. And it did! |
| 52 | + |
| 53 | +<br> |
| 54 | + |
| 55 | +# Spoiler Alert |
| 56 | + |
| 57 | +WARNING: if you would like to try to solve the same codewar challenge, don't scroll any further. |
| 58 | + |
| 59 | +<br> |
| 60 | + |
| 61 | +## Solution |
| 62 | + |
| 63 | +```elixir |
| 64 | +defmodule Shepherd do |
| 65 | + # This form of the function was taken directly from the example at elixir |
| 66 | + # school. |
| 67 | + def count_sheeps([]), do: 0 |
| 68 | + # Next, we write a form of the function that pattern matches the first |
| 69 | + # element to see if it is "true". If it is, we add 1. |
| 70 | + def count_sheeps([true | tail]), do: 1 + count_sheeps(tail) |
| 71 | + # All other elements will count as 0. |
| 72 | + def count_sheeps([_ | tail]), do: 0 + count_sheeps(tail) |
| 73 | +end |
| 74 | +``` |
| 75 | + |
| 76 | +## My First Elixir App |
| 77 | + |
| 78 | +When I first learned ruby over 6 years ago, my first ruby app was a cli blackjack game. If it was good enough to start learning ruby, it should be good practice for elixir as well. |
| 79 | + |
| 80 | +The app is still a work in progress, but here is what I've done so far. I started by creating a new `mix` project which was probably a bit over kill. /shrug The first thing black jack needs is cards. So, I started by creating a Card Struct. |
| 81 | +`lib/card.ex` |
| 82 | + |
| 83 | +```elixir |
| 84 | +defmodule Card do |
| 85 | + defstruct suit: "", value: "" |
| 86 | +end |
| 87 | +``` |
| 88 | + |
| 89 | +And each card will be part of a Deck, so let's go ahead and create a Deck struct too. It was at this point, I felt like my Object Oriented Programming mindset was having an affect on the way I was writing elixir, but I kept going. |
| 90 | + |
| 91 | +`lib/deck.ex` |
| 92 | + |
| 93 | +```elixir |
| 94 | +defmodule Deck do |
| 95 | + # I used Module constants to specify the different suits and the card values. |
| 96 | + @suits [:heart, :diamond, :club, :spade] |
| 97 | + @values ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"] |
| 98 | + |
| 99 | + # To generate the deck, we flat_map the @suits List using the shorthand |
| 100 | + # function syntax to pass the suit into the `Deck.build_suit/1` function. |
| 101 | + # Then, the List returned from the flat_map is shuffled using `Enum.shuffle` |
| 102 | + def generate do |
| 103 | + @suits |
| 104 | + |> Enum.flat_map(&(build_suit(&1))) |
| 105 | + |> Enum.shuffle() |
| 106 | + end |
| 107 | + |
| 108 | + # the `Deck.build_suit/1` function maps over the @values List and creates |
| 109 | + # %Card structs using the given suit and card value. |
| 110 | + defp build_suit(suit) do |
| 111 | + @values |> Enum.map(fn (value) -> %Card{ suit: suit, value: value } end) |
| 112 | + end |
| 113 | +end |
| 114 | +``` |
| 115 | + |
| 116 | +Not a bad start. However, I spun my wheels trying to figure out how I would store the `state` of the game, and handle the initial deal. I was finally able to scrap together something that worked, but it's definitely subject to change. |
| 117 | + |
| 118 | +```elixir |
| 119 | +defmodule Game do |
| 120 | + # I figured I could use a %Game struct to store the players and the current game's cards. |
| 121 | + # This will allow me to keep up with what a player's hand is as well as what |
| 122 | + # cards are still in play. |
| 123 | + defstruct players: [%Player{name: "player_one"}, %Player{name: "dealer"}], cards: Deck.generate() |
| 124 | + |
| 125 | + # With recursion on my mind from the earlier CodeWar Challenge, I created a |
| 126 | + # recursive deal function that would deal cards from the deck until each play |
| 127 | + # had their initial 2 cards. |
| 128 | + def deal(game) do |
| 129 | + cond do |
| 130 | + # The first conditional checks to see if all players have 2 cards |
| 131 | + Game.cards_dealt(game.players) -> game |
| 132 | + # The catch all conditional will deal a card to the first player in the |
| 133 | + # %Game struct then call deal again. |
| 134 | + true -> game |> deal_card(hd(game.players)) |> deal |
| 135 | + end |
| 136 | + end |
| 137 | + |
| 138 | + # deal_card/2 handles a couple of things. |
| 139 | + # 1. We pattern match the first `card` from the %Game.cards and the rest of |
| 140 | + # the cards. This essentially pulls a card from the deck. |
| 141 | + # 2. We update the %Game.players by giving the first `card` to the given player. |
| 142 | + # 3. Finally, we update the %Game struct cards to the `tail` from the original game argument. |
| 143 | + def deal_card(%{cards: [card | cards]} = game, player) do |
| 144 | + game |
| 145 | + |> update_players(Player.add_cards(player, card)) |
| 146 | + |> case do |
| 147 | + game -> %{ game | cards: cards} |
| 148 | + end |
| 149 | + end |
| 150 | + |
| 151 | + def cards_dealt(players) do |
| 152 | + Enum.reduce(players, 0, fn player, acc -> acc + length(player.hand) end) == length(players) * 2 |
| 153 | + end |
| 154 | + |
| 155 | + defp update_players(game, player) do |
| 156 | + players = game.players |> Enum.filter(fn (p) -> p.name != player.name end) |
| 157 | + |
| 158 | + # ++/2 is slower, but it makes it easier to deal the cards |
| 159 | + %{ game | players: players ++ [player]} |
| 160 | + end |
| 161 | +end |
| 162 | +``` |
| 163 | + |
| 164 | +I'd love to hear any thoughts about how to handle this better. You can drop a comment down below, or open up a pull request at https://github.com/basicBrogrammer/blackjack_cli. Thanks for tuning in and I'll see ya tomorrow. |
| 165 | + |
| 166 | +<br> |
| 167 | + |
| 168 | +## Follow Me @ |
| 169 | + |
| 170 | +[Twitter](https://twitter.com/basicbrogrammer) | [Instagram](https://instagram.com/basicbrogrammer) |
0 commit comments