public class Episode 6: Developer Burnout

{

Tim & Tim discuss what developer burnout looks like, how to deal with it, and how to avoid it.

Subscribe

public void Summary()

{

Tim & Tim discuss what developer burnout looks like, how to deal with it, and how to avoid it.

}

public void HotSpot()

{

  • Coding Blocks – Awesome dev podcast
  • Go for a Walk or a Drive! Get up outta your chair!

}

}

public class Episode 5: Java & Risk Assessment with Chris Purdum

{

Chris Purdum is a Senior Software Developer at PayPal. He talks about his work on Fraud and Risk Assessment, and compares language features of Java and .NET with Tim and Tim.

Subscribe

public void Summary()

{

Chris Purdum

Chris Purdum is a Senior Software Developer at PayPal. He talks about his work on Fraud and Risk Assessment, and compares language features of Java and .NET with Tim and Tim.

 

}

public void Links()

{

  • C++ – Low-level, un”managed”, cross-platform programming language, predecessor of Java and C#
  • PayPal – Digital payment transactions
  • Java – Original cross-platform managed language, strong inspiration for C#, still used widely and being developed/improved
  • JVMs – Java Virtual Machines
    • Zing – Azul performance-tuned JVM
  •  Scala – Succinct multi-paradigm (OOP and Functional) language that can run on both the JVM and Javascript runtimes
  • Hadoop – Distributed computing framework

}

public void HotSpot()

{

  • JFR – Java Flight Recorder: gives detailed analysis and diagnostics of the code as it runs
  • FindBugs – Static analysis for Java
  • Music! (You know where to find it…)
  • Pluralsight – Developer and IT-centered online learning site, with courses and IQ quizzes for many languages, frameworks, and patterns.

}

}

public class Episode 4: Teaming Up!

{

Subscribe

public void Summary()

{

Tim & Tim discuss the challenges and rewards of becoming team members vs. working as a solo dev.

}

public void Links()

{

}

public void HotSpot()

{

  • Display Fusion – Advanced Multi-Monitor support tool.
  • PowerToys – Free Windows plugin for multimonitor, shortcut keys, and other simple tools.

}

}

public class Episode 3: Abstract Thoughts on Abstractions

{

Subscribe

public void Summary()

{

Tim & Tim pontificate on the layers of abstractions that both make coding possible, and sometimes get in the way.

}

public void News()

{

public void HotSpot()

{

  • NotePad++ – Replacement for NotePad that will open any file, search through text of all files, and even function as a simple code editor
  • DotNetFiddle.net – Online environment for writing and compiling simple .NET test programs. Embeddable in other applications!

}

}

public class Episode 2: Strong Opinions

{

The Tims discuss what good (and bad) code looks like to them. They lay out their own preferences, from broad-scoped architecture to detailed style guides.

Subscribe

public void Summary()

{

The Tims discuss what good (and bad) code looks like to them. They lay out their own preferences, from broad-scoped architecture to detailed style guides.

}

public void News()

{

}

public void Links()

{

}

public void HotSpot()

{

  • Jetbrains – Multiple tools for developers, including IDEs, code analysis, db management
  • Rider – Powerful cross-platform .NET IDE, based on Resharper code analysis engine

}

}

public class Episode 1: The Path to Being a Developer

{

Subscribe

public void Summary()

{

Tim Purdum and Tim Jay introduce themselves, what drove them to software development, and the unique paths they each took.

}

public void Links()

{

}

public void HotSpot()

{

  • Fody – Compile-time tool for .NET
  • .NET 5 – The unified future of .NET Framework, .NET Core, Mono, and .NET Standard

}

}

public class Lesson6_GoFish_Pt2

{

void StartingTheGame()

{

This is a continuation of Lesson5_GoFish_Pt1. The full project code can be viewed or downloaded at https://github.com/TimPurdum/GoFish. In the previous post, we created a model of all the pieces needed to play the game, namely a Deck of Cards and Players with Hands and Sets.

For this part, we will be describing and programming the action or gameplay. Since the project is short, we will do this all within the Program class, although you could certainly create a Game class and move most of the code there. Let’s start by instantiating two players (one human and one computer) and a deck of cards as class fields that can be accessed from any method. The readonly tag means that these objects cannot be recreated (written to) later, although the methods and properties within are still writable.

        static readonly Deck Deck = new Deck();
        static readonly Player AI = new Player();
        static readonly Player Player = new Player();

Now let’s look at our Main method. Of course, you could put the entire logic into this method, but that would be poor design, and harder to read. Instead, we will simply deal with the introduction, shuffling the deck, and starting a while loop to create the game turns.

        static void Main()
        {
            Console.WriteLine("Let's play Go Fish!");
            Console.WriteLine("Type the letter 's' to shuffle the deck.");
            if (Console.ReadLine()?.ToLower() == "s")
            {
                Console.WriteLine("Shuffling...");
                Deck.Shuffle();
                Console.WriteLine();
                Deck.Deal(new List{Player, AI}, 7);

                while (Deck.Count > 0)
                {
                    ShowHand();
                    Guess();
                    AIGuess();
                }

                GameOver();
            }
        }

Notice that just like in our ChatBot project, we are using Console.WriteLine and Console.Readline to communicate with the user. Of course, you don’t have to introduce the game at all, or you could make it more involved (get the player’s name, for example).

The while loop represents rounds of the game. In each round, first we will ShowHand to the player, so they know what cards they have. Then, they will make a Guess, and we will deal with the results of that guess. Finally, the computer player (AI) will make their AIGuess, and the loop starts over. It ends when the deck is empty.

}

void ShowYourCards()

{

Each turn, we want to tell the player what cards they are holding.

        static void ShowHand()
        {
            Console.WriteLine("Here is your hand:");
            foreach (var card in Player.Hand)
            {
                Console.WriteLine($" - {card.Rank} of {card.Suit}");
            }
        }

}

void PlayersTurn()

{

Now it’s time for the player to take a turn. For this, we need to use Console.ReadLine() again. The player must have that rank in their hand (and if they have none, they simply draw). If the card is found, it is removed from the AI’s hand and given to the player. If not, the player must draw.

        static void Guess()
        {
            if (Player.Hand.Count == 0)
            {
                Console.WriteLine("No cards, you must draw this turn!");
                Deck.Draw(Player, 1);
                return;
            }
            
            Console.WriteLine();
            Console.WriteLine("Ask me if I have a card...");
            Console.WriteLine("(e.g., Ace, Two, Three, Four, King...)");
            var guess = Console.ReadLine();

            while (Player.Hand.All(c => c.Rank.ToString().ToLower() != guess?.ToLower()))
            {
                Console.WriteLine("You may only guess card numbers that you already have.");
                Console.WriteLine("Ask me if I have a card...");
                guess = Console.ReadLine();
            }
            
            Console.WriteLine();
            var cards = FindCards(guess, AI);
            
            if (cards != null)
            {
                var message = $"You got {cards.Count} {cards.First().Rank}!";
                if (cards.Count > 1)
                {
                    message = $"You got {cards.Count} {cards.First().PluralName}!";
                }
                Console.WriteLine(message);
                
                Player.Hand.AddRange(cards);
                foreach (var c in cards)
                {
                    AI.Hand.Remove(c);
                }
                LayoutSets(Player);
                Guess();
            }
            else
            {
                Console.WriteLine("No! Go Fish!");
                Thread.Sleep(1000);
                Deck.Draw(Player, 1);
                var newCard = Player.Hand.Last();
                Console.WriteLine($"You drew the {newCard.Rank} of {newCard.Suit}");
                LayoutSets(Player);
            }
        }

There are two places here where we have called new methods. The first is FindCards, which will search the AI player’s hand for the rank that was guessed. We are making heavy use of LINQ lambda expressions here, which you can learn more about. Any and Where are two LINQ statements that allow you to query a collection, such as a List or Array. Within each one, we declare an internal variable c, which represents each Card in the List, similar to a foreach statement. If the rank isn’t found, we can simply return null, which means no List was found.

        static List FindCards(string guess, Player p)
        {
            var cardGuess = guess.Trim().ToLower();

            if (p.Hand.Any(c => c.Rank.ToString().ToLower() == cardGuess))
            {
                return p.Hand.Where(c => c.Rank.ToString().ToLower() == cardGuess).ToList();
            }
           
            return null;
        }

The second new method is LayoutSets. This is where we will check to see if the player has four matching cards, and if so, move those sets to the Sets List.

        static void LayoutSets(Player p)
        {
            Console.WriteLine();
            
            var numberGroups = p.Hand.GroupBy(c => c.Rank);
            foreach (var numberGroup in numberGroups)
            {
                if (numberGroup.Count() == 4)
                {
                    var numberCards = numberGroup.ToList();
                    p.Sets.Add(numberCards);
                    foreach (var c in numberCards)
                    {
                        p.Hand.Remove(c);
                    }
                }
            }
            
            if (p.Sets.Count == 0)
            {
                return;
            }

            ShowSets(p);
        }

Notice that both FindCards and LayoutSets take a Player as an argument. This means that we can reuse these classes when it comes to the AI’s turn.

}

void ComputersTurn()

{

Once the player has finished, we give the computer (AI) a turn. Using Thread.Sleep() here and there can give the game a more pleasant pace, as modern computers are so fast that they will run through all these commands in less than a second.

Of course, in order to guess, we have to have the AI choose a random card rank from its hand. Luckily, .Net has a function called Random() that can do just that!

        static void AIGuess()
        {
            if (AI.Hand.Count == 0)
            {
                Console.WriteLine("No cards, I must draw this turn!");
                Deck.Draw(AI, 1);
                return;
            }
            
            var randomGenerator = new Random();

            var guessCard = AI.Hand[randomGenerator.Next(0, AI.Hand.Count - 1)];
            
            Console.WriteLine("My turn.");
            
            Console.WriteLine($"Do you have any {guessCard.PluralName}?");
            Thread.Sleep(2000);
            Console.WriteLine();
            var cards = FindCards(guessCard.Rank.ToString(), Player);
            
            if (cards != null)
            {
                if (cards.Count == 1)
                {
                    Console.WriteLine($"Yes? I'll take that {guessCard.Rank}!");
                }
                else
                {
                    Console.WriteLine($"Yes? I'll take those {guessCard.PluralName}!");
                }
                
                AI.Hand.AddRange(cards);
                foreach (var c in cards)
                {
                    Player.Hand.Remove(c);
                }
                LayoutSets(AI);
                AIGuess();
            }
            else
            {
                Console.WriteLine("No? I have to draw...");
                Thread.Sleep(1000);
                Deck.Draw(AI, 1);
                LayoutSets(AI);
            }
        }

As mentioned above, we re-use FindCards and LayoutSets for both players. Code re-use is an important principal in good programming.

}

void GameOver()

{

Let’s wrap up the game by totaling up the sets laid out and declaring a winner!

        static void GameOver()
        {
            Console.WriteLine("Game Over!");
            var playerPoints = 0;
            Player.Sets.ForEach(set => playerPoints += set.Count);
            Console.WriteLine($"Your score is {playerPoints}");
            var aiPoints = 0;
            AI.Sets.ForEach(set => aiPoints += set.Count);
            Console.WriteLine($"My score is {aiPoints}");
            if (playerPoints > aiPoints)
            {
                Console.WriteLine("You Win!");
            }
            else if (aiPoints > playerPoints)
            {
                Console.WriteLine("I Win!");
            }
            else
            {
                Console.WriteLine("Tie Game!");
            }
        }

If you have cloned the GitHub GoFish repository, and made changes to your version, feel free to create a pull request, to share your improvements back to our main repo! Or if you have a variation (maybe a new card game), you can share a link in the comments below.

}

}

public class Lesson5_GoFish_Pt1

{

void LetsPlayAGame()

{

It’s time to tackle a more complex assignment. We will start with a simple card game, “Go Fish.”

If you’ve never played, take a look at the directions at https://www.bicyclecards.com/how-to-play/go-fish/. We will be using a simple version of these rules to make our game. And if you would like to download or view the completed code project, go to https://github.com/TimPurdum/GoFish.

}

void ModelTheWorld()

{

Object-Oriented Programming is excellent for creating models of the real world, which, after all, is made up of objects. Let’s start by creating a model of a deck of cards. Actually, though, a deck of cards is just a collection of card objects, and we already know how to create collections! So, let’s start by creating a card object. What defines a card?

  • Suit – The color and symbol of the card. Hmm…maybe we need to represent suits as an object too. (In some other card games, the red suits are interchangeable, as are the black suits, so color might be important as well.)
  • Rank – The number or name of the card.

One simple way to create a list of value options is to create an enum. We will create two enums, one for Suit:

namespace GoFish
{
    public enum Suit
    {
        Hearts,
        Diamonds,
        Spades,
        Clubs
    }
}

And one for Rank:

namespace GoFish
{
    public enum Rank
    {
        Ace,
        Two,
        Three,
        Four,
        Five,
        Six,
        Seven,
        Eight,
        Nine,
        Ten,
        Jack,
        Queen,
        King
    }
}

In VS Code, go to File => New File, and add the code above for Suit. Save as Suit.cs, and then repeat and create Rank.cs. You should see these files added to your sidebar explorer, as well as the tabs at the top.

Now let’s create our Card file, saving it as Card.cs. Add the code below. Notice that this is now a class, out of which we can create all our cards. I’ve also created a property called PluralName so that I can return a string representation of the rank in its plural form.

namespace GoFish
{
    public class Card
    {
        public Suit Suit;
        public Rank Rank;
        
        public Card (Suit suit, Rank rank)
        {
            Suit = suit;
            Rank = rank;
        }


        public string PluralName
        {
            get
            {
                if (Rank == Rank.Six)
                {
                    return Rank + "es";
                }

                return Rank + "s";   
            }
        }
    }
}

Once we have the Card class, it is easy to build a collection of cards into a Deck. But instead of creating a collection by hand, I am using inheritance to create a special SubClass of List. Notice in the class declaration line, : List<Card>. This means that a Deck will inherit all the properties and methods of a List, such as Add and Remove. But in addition, we can add our own methods, like Shuffle and Draw.

using System;
using System.Collections.Generic;
using System.Linq;

namespace GoFish
{
    public class Deck : List
    {
        readonly Suit[] suits =
        {
            Suit.Hearts,
            Suit.Clubs,
            Suit.Diamonds,
            Suit.Spades
        };

        public Deck()
        {
            for (var i = 0; i < 13; i++)
            {
                foreach (var suit in suits)
                {
                    Add(new Card(suit, (Rank)i));
                }
            }
        }


        public void Shuffle()
        {
            var rnd = new Random();
            for (var i = 0; i < Count - 1; i++)
            {
                Swap(i, rnd.Next(i, Count));
            }
        }


        public void Deal(List players, int numberOfCards)
        {
            foreach (var player in players)
            {
                player.Hand = new List();
                Draw(player, numberOfCards);
            }
        }


        public void Draw(Player player, int numberOfCards = 1)
        {
            for (var cardNum = 0; cardNum < numberOfCards; cardNum++)
            {
                player.Hand.Add(this.First());
                RemoveAt(0);
            }
        }


        public Card Peek()
        {
            return this.First();
        }


        void Swap(int a, int b)
        {
            var temp = this[a];
            this[a] = this[b];
            this[b] = temp;
        }
    }
}

}

void ReadyPlayerOne()

{

We will create one more data model class, to signify a Player. Very simply, each player needs to have a Hand and Sets, or laid down 4-of-a-kind matches. Rather than create subclasses like we did for Deck, Hand uses the default List<Card> definition. Sets, however, is a List of a List, which allows you to track multiple groups of cards.

using System.Collections.Generic;

namespace GoFish
{
    public class Player
    {
        public List Hand { get; set; }
        
        public List<list> Sets { get; } = new List<list>();
    }
}</list</list

In our next post, we will explore how to lay out the action of the game!

}

}

public class Lesson4_Classes

{

public void WeNeedMoreClass()

{

While it’s nice to see all of your code in one place, as programs get larger, organization can become a hassle with long files. Just as we split up our Program class into multiple methods, we can also split up our code into multiple class files.

Actually, a class is just another OOP encapsulation, and multiple classes could live in the same file.

This allows me to show you two classes functioning in dotnetfiddle (which is limited to one file for console apps), but it mostly defeats the purpose of separating the code into classes. Traditionally for larger projects, classes are saved each to their own file. In VS or VS Code, you can manage these files just like any other file-based application. Right-click over the Explorer sidebar and choose New C# Class from the dropdown.

Once you’ve named your new file, it should open for you in the editor, and you will see tabs at the top to switch between files.

Note that although the classes are in separate files, as long as the Namespace at the top of each file is the same, and they are in the same folder, they will have no trouble communicating with each other.

}

public void InstantiatingAClass()

{

Once you have created a class file, in order to access the code in that file from another class, such as Program, you must instantiate, or create, an object of that class type. The class itself is a description, but you could have as many objects created with that type as you wish.

To instantiate an object, you use the new statement, and save the result to a variable.

var classObject = new ExtraClassy();

Then, to access features (methods, properties) on that object, use the dot syntax.

classObject.ClassyString = "Set the string";

}

public void Constructors()

{

In the example above, the parentheses following ExtraClassy represent an empty Constructor. Every class has an empty constructor by default. However, sometimes we want to pass in parameters when instantiating an object, just like we pass data to methods.

Notice that a constructor, unlike a method, has no return type, and its name is the same as that of the class.

}

public void Properties()

{

Above, you’ve seen several examples of a new item called a property. Properties are designed as accessible variables that can be set or get (retrieved) from outside the class. This is in contrast to fields, which are used primarily within a class.

A property always requires either a getter or setter, and can have both as well. An auto-property is one with the simplest form of these accessors, { get; set; }. This tells us that the property can be simply written to and read from. Sometimes, more complex logic might be used in either the get or set block, in which case they are encapsulated in curly braces and include a return like a method. These properties are often paired with a private field, which actually stores the data.

}

public void DesignPrincipals()

{

Many amazing books and blog posts have been written about Object-Oriented Design principals, and it is not the purpose of this site to make you an expert in these. However, one simple design principal I have found extremely useful is to separate my classes into two groups: Data Transfer Objects (DTOs) and Business Logic classes. The examples on this page so far have been DTOs. That is, they exist primarily as a repository to store, transfer, and retrieve data, in the form of properties.

A Business Logic class, on the other hand, is one that does something, in the form of methods. In a short program, this might be all handled by the Program class, but it should be separated out into other classes for more complex designs.

}

}

public class Lesson3_ChatBot

{

void HelloAnyone()

{

We are going to create a simple chat bot, using the Console in DotNetFiddle or VSCode and a new command: Console.ReadLine().

Feel free to personalize the opening message and greeting. Can you add a second prompt? Maybe a follow-up question? (Typing anywhere in the code window will also reset the console output).

}

void DictionaryResponses()

{

To create a good list of responses to user input, it’s helpful to use a Dictionary, which is a new .NET Type, similar to the List, but with a matched pair of variables (key and value). Don’t forget to import System.Collections.Generic!

Here are some challenges for you

  1. Add more commands and responses to the dictionary.
  2. Can you make the program smart enough to understand both capitalized and uncapitalized strings? (hint: check for a method on the input variable by typing a period after and seeing what pops up)
  3. Use a for loop to have the program ask for commands more than once.
  4. Can you figure out a way to prevent the error message if someone types in an unknown command?

Don’t peek ahead until you’ve at least tried to solve #3-4!

}

void WhileLoops()

{

If you succeeded at creating a for loop for challenge #3 above, it probably looked something like this:

This saves us from writing out the code five times, but what if we want to keep going as long as the person is interested? That’s what a while loop is for. While loops continue as long as a certain variable or formula is true.

We’ll also take this time to introduce a new kind of variable, the field. The only difference between a regular variable and a field is that fields are created and maintained at the class level, so they are accessible from any method in that class.

}

void SafetyChecks()

{

No one likes seeing error codes pop up when running a program! To avoid the error that occurs when someone types in random commands, we need to understand why it is happening. In this instance, we have sent an input as a key to our Dictionary. The dictionary looks, but finds that key doesn’t exist. It doesn’t know what we want to do, so it throws an exception. Notice that even with the while loop code, the program terminates at the exception.

There are several ways to handle this problem. First, we can check to see if the key exists ahead of time. Luckily, the Dictionary class has a method for that purpose, called ContainsKey, that returns a boolean value to tell you whether or not to proceed.

The other approach is to use a Try/Catch block. The advantage of this approach is that we don’t need to check ahead, but we can still continue running if an exception is thrown. You can choose whether to print the exception, log it, ignore it, or have it trigger some other code. If there is no exception, the catch block is skipped.

}

void SaveYourWorkAndShare()

{

By now, hopefully you have lots of ideas of what your chat bot could do. Make sure to save your work! You can go to DotNetFiddle.Net and create a free account to save your fiddles.

Of course, if you are using VS Code, the files are already saved on your machine. If you want to share your local code, you should learn about using Git and GitHub with VSCode. Github also allows you to accept pull requests, or code contributions from other programmers.

Want to share your ideas with us? From DotNetFiddle or GitHub, you can simply copy the address of your code and paste it in a comment on this page! Let us know what your chatbot can do.

}

}