Assignment 1: Evil Hangman

Similar documents
Assignment 1: Evil Hangman

Assignment 2: Fun with Collections

Evil Hangman Project

CS103 Spring 2018 Mathematical Vocabulary

BEGINNER PHP Table of Contents

MITOCW watch?v=0jljzrnhwoi

CPSC 320 Sample Solution, Playing with Graphs!

Designing a Database -- Understanding Relational Design

Assignment 1: grid. Due November 20, 11:59 PM Introduction

CS103 Handout 50 Fall 2018 November 30, 2018 Problem Set 9

static CS106L Spring 2009 Handout #21 May 12, 2009 Introduction

Laboratory 5: Implementing Loops and Loop Control Strategies

Assignment 1: SmartPointer

Slide 1 CS 170 Java Programming 1 Testing Karel

CS02b Project 2 String compression with Huffman trees

Linked Lists. What is a Linked List?

Outline for Today. How can we speed up operations that work on integer data? A simple data structure for ordered dictionaries.

CS 31: Intro to Systems Caching. Martin Gagne Swarthmore College March 23, 2017

The Stack, Free Store, and Global Namespace

Skill 1: Multiplying Polynomials

Google Docs Website (Sign in or create an account):

Post Experiment Interview Questions

Formal Methods of Software Design, Eric Hehner, segment 24 page 1 out of 5

1 Getting used to Python

My Samsung Galaxy S5 For Seniors PDF

MITOCW watch?v=hverxup4cfg

Assignment #1: /Survey and Karel the Robot Karel problems due: 1:30pm on Friday, October 7th

In our first lecture on sets and set theory, we introduced a bunch of new symbols and terminology.

Mehran Sahami Handout #7 CS 106A September 24, 2014

Chapter 21a Other Library Issues

CS106A Handout 15 Winter 2015 February 4, 2015 CS106A Practice Midterm

const int length = myvector.size(); for(int i = 0; i < length; i++) //...Do something that doesn't modify the length of the vector...

CMPSCI 187 / Spring 2015 Hangman

(Python) Chapter 3: Repetition

Instructor: Craig Duckett. Lecture 07: Tuesday, April 17 th, 2018 Conflicts and Isolation, MySQL Workbench

Hi everyone. Starting this week I'm going to make a couple tweaks to how section is run. The first thing is that I'm going to go over all the slides

VISUAL GUIDE to. RX Scripting. for Roulette Xtreme - System Designer 2.0. L J Howell UX Software Ver. 1.0

Practice Midterm #2. Midterm Time: Monday, July 18 th, 7pm 9pm Midterm Location: Hewlett 200

Assignment #1: and Karel the Robot Karel problems due: 3:15pm on Friday, October 4th due: 11:59pm on Sunday, October 6th

Lab 4: Super Sudoku Solver CSCI 2101 Fall 2017

MITOCW watch?v=se4p7ivcune

Lab 4: Imperative & Debugging 12:00 PM, Feb 14, 2018

Week - 01 Lecture - 04 Downloading and installing Python

Introduction. Uploading and Syncing Files. Google Drive and Docs Uploading and Syncing Files. Uploading Files to Google Drive.

PROFESSOR: Last time, we took a look at an explicit control evaluator for Lisp, and that bridged the gap between

VISI ON CALC QuickStart Course

Problem A - EPIC. Line Width of Letters (# characters) EEEEEEEEEE EEEEEEEEEE EE EE EEEEEEEEEE EEEEEEEEEE EE EE EEEEEEEEEE EEEEEEEEEE

CS 3L (Clancy) Solutions and grading standards for exam 1

Notes to Accompany Debugging Lecture. Jamie Blustein

MITOCW watch?v=w_-sx4vr53m

Chapter01.fm Page 1 Monday, August 23, :52 PM. Part I of Change. The Mechanics. of Change

Programming assignment A

Copy Constructors and Assignment Operators

6.189 Project 1. Readings. What to hand in. Project 1: The Game of Hangman. Get caught up on all the readings from this week!

The following content is provided under a Creative Commons license. Your support

Part 1 Simple Arithmetic

How to Improve Your Campaign Conversion Rates

CS103 Handout 13 Fall 2012 May 4, 2012 Problem Set 5

Table of Laplace Transforms

How to approach a computational problem

Definition: A data structure is a way of organizing data in a computer so that it can be used efficiently.

Black Problem 2: Huffman Compression [75 points] Next, the Millisoft back story! Starter files

COMP 105 Homework: Type Systems

The following content is provided under a Creative Commons license. Your support

CS 210 Fundamentals of Programming I Spring 2015 Programming Assignment 4

So on the survey, someone mentioned they wanted to work on heaps, and someone else mentioned they wanted to work on balanced binary search trees.

Earthwork 3D for Dummies Doing a digitized dirt takeoff calculation the swift and easy way

Data Structures and Algorithms Dr. Naveen Garg Department of Computer Science and Engineering Indian Institute of Technology, Delhi.

Learning more about the internet will help students develop a better understanding of its endless possibilities.

MITOCW watch?v=yarwp7tntl4

CS 142 Style Guide Grading and Details

MITOCW watch?v=sdw8_0rdzuw


Arduino IDE Friday, 26 October 2018

Outline for Today. How can we speed up operations that work on integer data? A simple data structure for ordered dictionaries.

Android Programming Family Fun Day using AppInventor

CS 31: Intro to Systems Caching. Kevin Webb Swarthmore College March 24, 2015

The Dynamic Typing Interlude

Embedding Python in Your C Programs

Clickteam Fusion 2.5 Creating a Debug System - Guide

Introduction to Programming

Creating Programs C Programming Edition

In further discussion, the books make other kinds of distinction between high level languages:

You have three hours to complete this exam. There are 50 total points.

6.001 Notes: Section 8.1

Turing Machines Part Two

Web-Friendly Sites. Planning & Design 1

Member Initializer Lists

Here are all the comments that your Udacity Code Reviewer had about your code...

Mathematical Logic Part Two

It Might Be Valid, But It's Still Wrong Paul Maskens and Andy Kramek

Mathematical Logic Part Two

EE 355 Lab 4 - Party Like A Char Star

Lesson 4B Conditionals switch case. By John B. Owen All rights reserved 2011, revised 2014

The Domino Designer QuickStart Tutorial

A Tutorial for Adrift, Version 5

Blitz2D Newbies: Definitive Guide to Types by MutteringGoblin

CSC 148 Lecture 3. Dynamic Typing, Scoping, and Namespaces. Recursion

Laboratory 1: Eclipse and Karel the Robot

The Crypt Keeper Cemetery Software Online Version Tutorials To print this information, right-click on the contents and choose the 'Print' option.

Transcription:

CS106L Spring 2014 Handout #03 April 24, 2014 Assignment 1: Evil Hangman Due Sunday, May 11, 11:59 PM Introduction It's hard to write computer programs to play games. When we humans sit down to play games, we can draw on past experience, adapt to our opponents' strategies, and learn from our mistakes. Computers, on the other hand, blindly follow a preset algorithm that (hopefully) causes it to act intelligently. Though computers have bested their human masters in some games, most notably checkers and chess, the programs that do so often draw on hundreds of years of human experience and use extraordinarily complex algorithms and optimizations to out calculate their opponents. While there are many viable strategies for building competitive computer game players, there is one approach that has been fairly neglected in modern research cheating. Why spend all the effort trying to teach a computer the nuances of strategy when you can simply write a program that plays dirty and wins handily all the time? In this assignment, you will build a mischievous program that bends the rules of Hangman to trounce its human opponent time and time again. In doing so, you'll cement your skills with the STL containers and will hone your general programming savvy. Plus, you'll end up with a highly entertaining piece of software, at least from your perspective. In case you aren't familiar with the game Hangman, the rules are as follows: 1. One player chooses a secret word, then writes out a number of dashes equal to the word length. 2. The other player begins guessing letters. Whenever she guesses a letter contained in the hidden word, the first player reveals each instance of that letter in the word. Otherwise, the guess is wrong. 3. The game ends either when all the letters in the word have been revealed or when the guesser has run out of guesses. Fundamental to the game is the fact the first player accurately represents the word she has chosen. That way, when the other players guess letters, she can reveal whether that letter is in the word. But what happens if the player doesn't do this? This gives the player who chooses the hidden word an enormous advantage. For example, suppose that you're the player trying to guess the word, and at some point you end up revealing letters until you arrive at this point with only one guess remaining: D O B L E There are only two words in the English language that match this pattern: doable and double. If the player who chose the hidden word is playing fairly, then you have a fifty-fifty chance of winning this game if you guess 'A' or 'U' as the missing letter. However, if your opponent is cheating and hasn't actually committed to either word, then there is no possible way you can win this game. No matter what letter you guess, your opponent can claim that she had picked the other word, say that - 1 -

your guess is incorrect, and win the game. That is, if you guess that the word is doable, she can pretend that she committed to double the whole time, and vice-versa. Let's illustrate this technique with an example. Suppose that you are playing Hangman and it's your turn to choose a word, which we'll assume is of length four. Rather than committing to a secret word, you instead compile a list of every four-letter word in the English language. For simplicity, let's assume that English only has a few four-letter words, all of which are reprinted here: ALLY BETA COOL DEAL ELSE FLEW GOOD HOPE IBEX Now, suppose that your opponent guesses the letter 'E.' You now need to tell your opponent which letters in the word you've picked are E's. Of course, you haven't picked a word, and so you have multiple options about where you reveal the E's. Here's the above word list, with E's highlighted in each word: ALLY BETA COOL DEAL ELSE FLEW GOOD HOPE IBEX If you'll notice, every word in your word list falls into one of five word families: ----, which contains the word ALLY, COOL, and GOOD. -E--, containing BETA and DEAL. --E-, containing FLEW and IBEX. E--E, containing ELSE. ---E, containing HOPE. Since the letters you reveal have to correspond to some word in your word list, you can choose to reveal any one of the above five families. There are many ways to pick which family to reveal perhaps you want to steer your opponent toward a smaller family with more obscure words, or toward a larger family in the hopes of keeping your options open. For this assignment, in the interests of simplicity, we'll adopt the latter approach and always choose the largest of the remaining word families. In this case, it means that you should pick the family ----. This reduces your word list down to ALLY COOL GOOD and since you didn't reveal any letters, you would tell your opponent that his guess was wrong. Let's see a few more examples of this strategy. Given this three-word word list, if your opponent guesses the letter O, then you would break your word list down into two families: -OO-, containing COOL and GOOD. ----, containing ALLY. The first of these families is larger than the second, and so you choose it, revealing two O's in the word and reducing your list down to COOL GOOD But what happens if your opponent guesses a letter that doesn't appear anywhere in your word list? For example, what happens if your opponent now guesses 'T'? This isn't a problem. If you try - 2 -

splitting these words apart into word families, you'll find that there's only one family: the family ---- containing both COOL and GOOD. Since there is only one word family, it's trivially the largest, and by picking it you'd maintain the word list you already had. There are two possible outcomes of this game. First, your opponent might be smart enough to pare the word list down to one word and then guess what that word is. In this case, you should congrat - ulate him that's an impressive feat considering the scheming you were up to! Second, and by far the most common case, your opponent will be completely stumped and will run out of guesses. When this happens, you can pick any word you'd like from your list and say it's the word that you had chosen all along. The beauty of this setup is that your opponent will have no way of knowing that you were dodging guesses the whole time it looks like you simply picked an unusual word and stuck with it the whole way. The Assignment Using only standard C++ (that is, without using any of the CS106B/X libraries), your assignment is to write a computer program which plays a game of Hangman using this Evil Hangman algorithm. In particular, your program should do the following: 1. Read the file dictionary.txt, which contains the master word list. 2. Prompt the user for a word length, prompting as necessary until she enters a number such that there's at least one word that's exactly that long. That is, if the user wants to play with words of length -42 or 137, since no English words are that long, you should reprompt her. 3. Prompt the user for a number of guesses, which must be an integer greater than zero. Don't worry about unusually large numbers of guesses after all, having more than 26 guesses is not going to help your opponent! 4. Prompt the user for whether she wants to have a running total of the number of words remaining in the word list. This completely ruins the illusion of a fair game that you'll be cultivating, but it's quite useful for testing (and grading!) 5. Play a game of Hangman using the Evil Hangman algorithm, as described below: 1. Construct a list of all words in the English language whose length matches the input length. 2. Print out how many guesses the user has remaining, along with any letters the player has guessed and the current blanked-out version of the word. If the user chose earlier to see the number of words remaining, print that out too. 3. Prompt the user for a single letter guess, reprompting until the user enters a letter that she hasn't guessed yet. Make sure that the input is exactly one character long and that it's a letter of the alphabet. 4. Partition the words in the dictionary into groups by word family. - 3 -

5. Find the most common word family in the remaining words, remove all words from the word list that aren't in that family, and report the position of the letters (if any) to the user. If the word family doesn't contain any copies of the letter, subtract a remaining guess from the user. 6. If the player has run out of guesses, pick a word from the word list and display it as the word that the computer initially chose. 7. If the player correctly guesses the word, congratulate her. 6. Ask if the user wants to play again and loop accordingly. It's up to you to think about how you want to partition words into word families. Think about what data structures would be best for tracking word families and the master word list. Would a vector work? How about a map? A stack or queue? Thinking through the design before you start coding will save you a lot of time and headache. Efficiency Concerns The program you will be writing maintains a grand illusion: it pretends to play a game of Hangman, but instead does something more nefarious behind-the-scenes. Consequently, you should try to make your program as responsive as possible. If players have to wait three or more seconds after entering a letter, they're almost certain to notice that something is awry. The illusion will be broken, and the beauty of your program will be lost. Although efficiency is important in Evil Hangman, never forget the wisdom of programmers past: * Premature optimization is the root of all evil. There is an inherent tradeoff between readable code and optimized code. Code that is clean and easy to follow often contains subtle sources of inefficiency, while code that is optimized is often impossibly difficult to read. As an extreme example, consider the following piece of C code, known as Duff's Device: int n = (count + 7) / 8; switch (count % 8) { case 0: do { *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; while (--n > 0); This code is entirely equivalent to this (much more readable) C++ statement: * This has been attributed to both Don Knuth and Tony Hoare, both of whom are legends in the field, so it should carry some weight! Retrieved from http://foldoc.org/duff%27s+device - 4 -

copy(from, from + n, to); On some machines, Duff's Device slightly performs outperforms a hand-written for loop or a call to the copy algorithm due to the way that the processor handles loops. Of course, Duff's Device is impossibly hard to read, and even the best of programmers would have no way of deciphering it without extensive commenting. As you write your Evil Hangman implementation, try to get the program working before you worry about runtime efficiency. If you try to optimize the code as you write it, you are almost certainly going to end up with code that is harder to read and maintain, making debugging a nightmare. Moreover, if you optimize as you go, there's no guarantee that your optimization will have any noticeable effect. If you spend a long time optimizing parts of the code that aren't actually contributing to the overall runtime, you'll have complicated the code with little to show for it. The moral of the story is this: first get your program working, then worry about making it run quickly. Chances are, your program will be fast enough the first time you write it and you won't need to optimize it. If for some reason this isn't the case, feel free to send me an email and I can try to offer some advice. Advice, Tips, and Tricks There is no starter code for this program, and you'll be building it from scratch. Consequently, you'll need to do a bit of planning to figure out what the best data structures are for the program. There is no right way to go about writing this program, but some design decisions are much better than others (e.g. you can store your word list in a stack or map, but this is probably not the best option). Here are some general tips and tricks that might be useful: 1. Letter position matters just as much as letter frequency. When computing word families, it's not enough to count the number of times a particular letter appears in a word; you also have to consider their positions. For example, BEER and HERE are in two different families even though they both have two E's in them. Make sure your representation of word families can encode this distinction. 2. Watch out for gaps in the dictionary. When the user specifies a word length, you will need to check that there are indeed words of that length in the dictionary. You might initially assume that if the requested word length is less than the length of the longest word in the dictionary, there must be some word of that length. Unfortunately, the dictionary contains a few gaps. The longest word in the dictionary has length 29, but there are no words of length 26 or 27. Be sure to take this into account when checking if a word length is valid. 3. Don't explicitly enumerate word families. If you are working with a word of length n, then there are 2 n possible word families for each letter. However, most of these families don't actually appear in the English language. For example, no English words contain three consecutive U's, and no word matches the pattern E-EE-EE--E. Rather than explicitly generating every word family whenever the user enters a guess, see if you can generate word families only for words that actually appear in the word list. - 5 -

4. Be careful when iterating over containers while removing elements. If you remove an element from a container, you invalidate all iterators that point to that element. Invalid iterators may point to garbage data, and their * and ++ operators are not guaranteed to work as expected. If you're working with a sequence container (list, vector, or deque), you can avoid issues with invalidated iterators by updating the value of the iterator you pass to erase to the iterator returned by erase. For example: Extensions while(itr!= mycontainer.end() { if (/* some condition */) itr = mycontainer.erase(itr); else ++itr; When working with associative containers (map, set), if you have an iterator itr to an element that you want to remove, and you're also using itr inside of a loop, you can prevent itr from being invalidated by using this technique: while(itr!= mycontainer.end()) { if (/* some condition */) { containertype::iterator toremove = itr; ++itr; // Advance itr. mycontainer.erase(toremove); // Remove element. /* Otherwise, we didn't advance itr yet, so do it here. */ else ++itr; Also, do be aware that removing elements at arbitrary positions inside of an STL vector or deque can be very slow. In fact, if you are planning on removing a large number of elements from a vector or deque, you are better off creating a brand-new container holding all of the elements you'd like to retain, then overwriting the original vector or deque with this new one. The algorithm outlined in this handout is by no means optimal, and there are several cases in which it will make bad decisions. For example, suppose that the human has exactly one guess remaining and that computer has the following word list: DEAL TEAR MONK If the human guesses the letter 'E' here, the computer will notice that the word family -E-- has two elements and the word family ---- has just one. Consequently, it will pick the family containing DEAL and TEAR, revealing an E and giving the human another chance to guess. However, since the human has only one guess left, a much better decision would be to pick the family ---- containing MONK, causing the human to immediately lose the game. More generally, picking the largest of the remaining word families is not necessarily the best option. After you implement this assignment, take some time to think over possible improvements to the algorithm. You might weight the word families using some metric other than size. You might consider having the computer look ahead a step or two by considering what actions it might take in - 6 -

the future. * If you implement something interesting, feel free to include it with your solution... I'd love to see what you've cooked up! * This idea of looking ahead generalizes to a strategy called minimax, which can be shown to play a theoretically perfect game. Researching and implementing a minimax search can make your player substantially more powerful and would be an excellent way to show off your CS wizardry. - 7 -