[Silence] >> Chris Gerber: Welcome. My name is Chris Gerber and this is CS 76. Tonight we'll be doing Lab 2. We're going to start off with a walkthrough of the next project which is Evil Hangman. So we'll start out with an overview of the project. We're going to talk about the equivalence classes which are sort of key to how Evil Hangman is going to work. We'll talk about setting up the project, how we're going to get input in the application, how we can use property lists within the project and finish up with just a little bit about how you can store setting in your application. So first of all, Evil Hangman. The goal of the game is to try to make the player lose and the way we're going to do that is we're just going to cheat. How we're going to cheat is after each guess we're going to select the longest possible list of words that is still available after that guess. This will make more sense as we get into it. So let's say we start with a very short list. We've got these seven items. I've got cat, cot, cow, dog, dot, tag and tot. So I have a minimum number of letters. We've only got maybe a half dozen letters here to work with. What are we going to do? So the player is going to come up and guess a letter. So let's say they've guessed the letter T. For each of the words we can then map what that guess would do to what the board would look like at that time. So if the word was cat and they guessed T they would have gotten the T in the third location and the first two would still be blank. If the word was actually cow and they had guessed T they wouldn't have any letters matching yet. If the word was actually tot they would get T-T. So you get this idea of if you'd played hangman before as you guess letters you fill in the blanks, so the possibilities after all this are we could end up with --T. We could end up with ---. We could end up with T-- or we could end up with T-T. So there's really only four possible outcomes at this point in the game no matter what the words were to start with. Moving from there we now have these four different classes and we can look at them. The words cat, cot and dot have all mapped to --T. So there are three cases that we can get to in that case. If it was cow or dog it would have matched to ---. So there are only two possibilities in that case. So obviously what we want to do at this point is pick what gives us the most opportunities. So if we look at this choice we've got three possible words still in the list so even though here we might not be showing that any letter has matched it's not as strategic a location for us to end up so what we'd rather do is show the user --T and still have three possible outcomes to work with. Does this basic idea sort of make sense? So then we can continue. We've got this new list and the person guesses the letter C. We've got these three conditions they map out to C-T or --T. Repeat the process. We see that there are now two cases for C-T and that is still our best case situation, so we'll pick that and continue on. If they guess A we get to a special case now. Each one is going to map to one condition but we definitely want to pick the best equivalence class for us which is cot because the user has not won. So we are going to hit situations where potentially the number of words in each list is the same. If there's no clear outcome we should just do something sort of random. If there is a clear outcome we should try to make the player lose. This is still following? It's still good? All right, so how do we actually do this? I mean, the idea makes sense. You can get a little sense of how you would play this with your friends if you were working on paper. If we go out to Wikipedia we'll see this, equivalence classes. "The equivalence class of an element A is denoted, bracket A, may be defined as the set of elements that are related to, A by tilde." This does not tell me a whole lot. What this basically translates to is you should define a set of words sharing a given letter at a given location and the other thing that comes out of it actually is order matters. So --T is treated differently than T--. So what would this look like in code? It would have been great but X code has no idea what NS equivalence class is. We just can't get it for free in this case, so we have to think about how we would actually implement this. So we've got a few options. We've heard about some of these classes and type, classes and class, so we have things like NS mutable dictionaries, NS mutable arrays, there's also something called an NS mutable set. As you remember the dictionaries map keys to values. An array is just a list of items but they're in a specific order. A set is different than those two. It's more like an array in that it's a list of items but the order of the items is not preserved. It's just a collection of items. So if we think about what we're going to do here we can start mapping this out in pseudocode. For each word in the set we need to determine what the equivalence class is for the word and then we can add that word to that equivalence class. So if the word was cat we've come up and guessed T we come to the equivalence class --T. We can then say for the equivalence class name --T I can add cat to that list. When we get done we're going to determine the largest equivalence class. We can remove all the other words because they're out of play at this point and then we have to update the user interface so the player knows what's going on in the game. I think I've already covered all that so we're going to skip that. So what should we be thinking about as we're implementing this? So time and space are things we should always about thinking about when we're writing our code. In this case when we think about time we have to think about iterating over lists. That's going to be an a very slow process. If we can do something that actually uses an index it will help the performance a lot, so we should look into the fact that we could index into arrays and dictionaries very quickly so that might drive your decisions. Another thing to think about and this will make more sense later I would consider that loading plists from storage is actually a slow process. If we can keep stuff in memory that is overall going to help performance, of course then we have to start thinking about our space considerations. So words.plist is a pretty big file. The file we're going to give you is just shy of 250 thousand words. It's still just text. It's not huge but we're dealing with a device that actually doesn't have a lot of storage. So that's something to think about. We'll want to keep our data structures as small as possible and we're also going to have to keep in mind that we don't want to keep things in memory longer than needed so we'll have to trade that off versus loaded from storage is slow. How long should we keep things in memory? You'll remember that when memory gets low IOS will actually warn us so perhaps we can keep things in memory for a while, be optimistic but if we're told that we have to give back memory that might be the time that we actually have to make a change and then keep track of what state we're in. So just a few things for you to ponder as you start working through this exercise. One thing that you should keep in mind though is this quote from Donald Knuth. If you don't know who he is you should look him up. He's a good guy. He said, "We should forget about small efficiencies, say about 97 percent of the time: Premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3 percent." So certainly we want to do what we can to squeeze out every last bit of memory but that should not be your first goal. Your first goal should be getting things into memory, getting things basically working and if you can start tweaking things afterwards that's great or if you start seeing that things are particularly slow then it's time to start thinking about things. You have question? >> I just want [inaudible] for a second, particularly if you could go back to that last slide. I'm not really sure I understand. We're iterating over all of the words in the collection and determining an equivalence of each of them why is that process slower than indexing arrays or dictionaries assuming that they are all consecutively arranged [inaudible] >> Chris Gerber: Okay, so the question is around why is iterating over the lists going to be slower than indexing into the arrays the way we're working? So one thing you might want to think about out there is we've got 250 thousand words approximately. All those words are different lengths and if you know that the length of the word is for example, eight characters you might only want to work with a subset of the list that is only eight character words and you can have that indexed out to a chunk of memories so you're only iterating over that subset and then as equivalence classes get thrown out you can shift yourself down to a smaller list of words so you're not iterating over 250 thousand every time or perhaps ten thousand then five thousand then two thousand. That's sort of more of where I was going there. Does that make is sense? >> Yeah, you'll be chunking around with more because you would be only picking out words-- >> Chris Gerber: --of certain length or have already been excluded. Yeah. All right? Yeah, another question? >> [Inaudible question] >> Chris Gerber: Yeah, so the question is around setting the length of the word. If you look at the specification, what we've specified is we'll be using utility application which sort of has a front side and a back side. The front side will be the playing of the game. The back side will be for configuring the game and one of the things you'll be implementing is a slider that sets the length of the word that the user will be playing with at that time and every time they restart the game until they change the length of the word it will continue to use that word length. So we'll be setting that as part of our settings which might be preloaded from disk, persisted for the next time they play, etcetera. Things to think about there. Does that make sense? Okay and did you have a question? >> Does it matter how long we set the amount it can change to, five to ten letters? >> Chris Gerber: So the question is does it matter how long, what the choices are for length of word? And what we'll ask you to do is assume that it is based on what you find in the file. So as you first load the file you'll want to look through and figure out what the shortest and longest words are in the file and that will be the size of your slider. Another question. >> [Inaudible question] >> Chris Gerber: So the question is as we first load a file into memory or as we first look at the plist we need to load the entire thing into memory and is that a concern? That is an unfortunate situation that we have to deal with. You will have to load everything into memory once and then from there you can decide how much you are going to retain in memory as the application runs. >> Are we going to have a choice as to data structure that we are going use to store the information in memory like [inaudible] >> Chris Gerber: The question is around what data structure we're going to use to store this in memory and that is completely open to your interpretation of the problem and how you actually want to implement it. You should probably stick with some of the common built in data types but it's up to you to determine what you want to do. >> And as a side note you don't have to load it in [inaudible] plists. You're free to pre-process it in whichever ways you want but any [inaudible] of those words support it. >> Chris Gerber: That was a great point if the cameras didn't pick it up. You don't need to load plist in your application. You can pre-process the file and do something else to deal with that so I think the specification actually mentions parenthetically that you could use something like a SQL-like database or you could somehow otherwise pre-process the file so you're using smaller subsets as you were. Another question? >> [Inaudible question] >> Chris Gerber: So the question is around the strict definition of equivalence classes being symmetric, transitive, I missed the other one. >> Reflexive? >> Chris Gerber: Reflexive. I honestly don't have a good answer on that one. Anything else? All right, so just to summarize, at every point you want to leave yourself the most number of options that you can. If at all possible you don't want the player to win and if you have the opportunity you should think about how you might optimize our very simple algorithm further. So getting down to real details, how do we get started? We're going to be using a utility application and that's one of the standard templates that you'll see in Xcode. It contains two controllers, a MainViewController and a FlipsideViewController. Typically, the Flipside Controller is used for storing configurations, storing settings. For those that have played with an iPhone you can pick up something like the Apple Weather App but I can also on the fly, too many windows. I know what happened. I have to exit my show first. All right, now I've got code all over the place. Get that out of the way, get this out of the way. All right, oh, I don't have it there. After all that I can't even show you what I was going to show you. That's what--now I've lost PowerPoint. All right, so for now I will assume that you can picture sort of what the application looks like and we'll move forward from there. All right, with the utility application one of the things we'll have to use is this idea of delegates and protocols. So what a delegate is it identifies an object that will handle an action on your behalf rather than you handling the action. And where we're going to see this in this particular case is the FlipsideViewController requires a delegate which is the MainViewController and this allows the MainViewController to have a piece of code that runs that is passed to it from the FlipsideViewController. So any changes you make on the Flipside can be communicated back to the MainViewController. So you've change the slider. It's essentially got call back as we saw on Java script where it can call back to MainViewController and say "Hey, this was updated." To use a delegate you have to implement a protocol. In this case it will be a FlipsideViewController protocol that it knows what to do and the protocol essentially just guarantees that the methods you specify will be implemented. The protocol itself will look something like this. You'll have at protocol, some name for the protocol. You can have a number of methods that could either be voids, they could return values, they could be passed in values and then at end the at the end of the method. All right, so let's see if we can actually take a look at the utility application. So I'm going to go from Xcode, File, New Project. Utility application can be found under IOS application. It'll ask you for a product name. So I can give it a name. Your organization can be anything. I've just been using my name. I believe the spec asks you to edu.Harvard as your company identifier. We don't need to use class prefix at this time and for this I'm just going to use iPhone. You can also specify iPad or Universal. We're not going to use story boards at this time but we will talk about them a bit later tonight. We don't need to use core data at this time. Core data is essentially a back in data store that you can use with your applications. We are going to use automatic reference counting and for tonight I'm not going to include any unit tests. I'll just assume I know what I'm going and my code is going to run. It'll ask you for a place to save your code and you can create a local get repository or not. I personally like to because I use get all day and am very familiar with the source control but it's not strictly necessary for what we're doing and you can hit create. So that will give us our new application. As we mentioned there's the MainViewController. There's the FlipsideViewController. Each of those is also going to have a NIB file so this should look vaguely familiar from lecture. If you look at the MainViewController you'll see it's not very complicated. There is one little button down here. Let's see if I can, let me put my simulator-- [On Screen Activity Being Performed] So I don't have the fancy border on here but you can see it's the same window. It's got the information button. As we mentioned, all this is going to do is flip to the other side. This is where we would see things like settings. We hit done. We flip back to the first side. To round that out you'll see the FlipsideViewController has the done button entitled that we saw and you can look through the actual implementations. There's not a ton here. Things like view did load, view did receive memory warning, we've seen before and obviously they're blank so they could be deleted at this time. If we actually look at the header first we'll see that there is a FlipsideViewController delegate as we mentioned so that we can do something on behalf of the flip side. You'll see that's actually implemented here. The method that's promised through that protocol is a FlipsideViewController did finish method. Yeah, sorry. Which one is it? Control, Option, INT. Ah, there we go. Of course, my window has gone so far over--now I can't--there we go. So FlipsideViewController did finish. It's going to take the controller as it's argument. In this case all it's going to do is do an animated close but this is our opportunity to have access to the variables that are in scope of the FlipsideViewController and do whatever we need to do. So if there was a slider that had a value of ten we could take that ten and change some variable in our local scope as well. Does that basic idea make sense? Okay and the FlipsideViewController is very straight forward. We've got the actual protocol here that we implemented which is the FlipsideViewController did finish. This is the promise that we made when we said we would implement this delegate. And the only actual line of curve that we might care about here is that when we click the done button the FlipsideViewController says it is going to finish. All right, that's all I wanted to cover there. All right, so how are we actually going to get input in the application? So ideally we want to see keyboard popped up. We want to hit a letter. It's going to process the letter, think about it. The way to get input in an IOS application is to use a UI text field. UI text field itself is great if you're just doing traditional stuff but if you want to do more you can use a UI text field delegate and that gives you additional functionality. This also lets you do things like actually make the text field hidden which is probably what we'd like in this application. We want to listen to the keyboard. We want to respond to the key presses but we don't want necessarily that text appearing on the screen in a box somewhere. We want to process it in a more intelligent manner. So if we use this UI text field delegate we get some additional methods that we can implement here. We get text field should return. This is what happens when someone hits the done button on the keyboard and then we can do whatever is appropriate there like perhaps hide the keyboard. We've got text field should begin editing which is when the user is about to edit text. We can do whatever we need to set up our application then. And there's a text field should end editing which is right as the field is about to lose focus. So now I've got a very quick demo about what we can do with UI text field. That is, this is it. [On Screen Activity Being Performed] All right, so I've made a simple utility application here. I've got a text field and a label and that's it. And what I'd like to have happen is when the application launches first of all it doesn't make much sense for the label to actually say the word label so I want to change that. The field for entering text is currently visible so I want to hide that and then I have to deal with how does this all get wired up? So, let's look at that, the header first. We're just off the screen. All right, so you'll remember from class we've got these two new things called IB outlets and IB actions. IB outlets let us control the properties for, sorry, let us set properties from the UI and IB actions let us allow us to respond to actions occurring within the UI. So I've got, as you've noticed the label and a text field. In my application I've set up a label and a text field and referred to them as text, label and keyboard. And again just, you'll remember there's these little dots that appear and I've prewired these up for us and we'll see that in the UI side. I've also created a new IB action here for handling key presses. So this is going the other way. Rather than updating the UI I'm listening to the UI and responding to what's happening. We then switch over to the actual implementation. We can see if we can get that a little more centered. I've done a few things here. So first when the application is first loading you'll remember we see a view did load event occur. So I've done a few things here. I'm going to set the text label text to blank. I can send a message to the edit field that I've called keyboard and tell it to set its hidden property to yes. Then the other thing I've done you'll remember from class is I can do, send the keyboard to become first responder message. And what that message is going to do is basically tell it that it is the primary control and when a text field is the primary control the first thing that it wants to do is display the keyboard. So that's going to pop up the keyboard for me. The other method he I implemented is this handle key press method which you saw I created as an IB action. And what I'm doing here is when a UI text field has received key presses it appends to a property called text. So what I'm going to do is check the length of that text, see if it has become greater than zero, so something has been typed in. If it is I'm going to change the text of the label by setting the text to what the text of the label was, create a new string by appending a string and it scrolls off the side here. Let me see if I can see it, yeah, to whatever the keyboard text is. So I'm taking what was in the field I'm appending whatever the keyboard was and then I'm going to set the value set in the edit field to just blank, to zero it back out. So this, every time it hears a key press it will take it, append it to what was already in the field. >> Chris, will we have access to these slides? >> Chris Gerber: Yes, I will be--the question was will we have access to these slides? I will be uploading these slides and the source code that I'm showing tonight to the CS 76 dot website. It should be up before I go to bed but that could be any time, hopefully, not five. All right, the other thing I wanted to show is back in--in the view controller you'll see that I've wired the text label to file's owner and it's not obvious that file's owner is on the screen. I know what the dot, dot, dot means in this case. That allows the controller to have right to that field and for the text field I had to set two things. Can I get them both to show? Well, the first one is editing changed so every time the contents of the field are changing I am going to tell the file's old owner to, it doesn't fully show but call my handle key press method. Let's see if I can get the other one for it. The other one is down here. Because I also need to be able to empty out the field I've set the keyboard to be wired up to file's owner as well and that allows me to access those properties. So putting it all together we can find our play button and the application is gone off the bottom of the screen but what we care about is at the top even though you can't see it and the fact that you can't see it is a good thing, all right. It doesn't show us the word label which is what we wanted as a starting point. It doesn't show us that edit field but I can't see the keyboard. I don't think it's running right. Let's try this again. I have a breakpoint in my application. As you may remember if you put in a breakpoint your code will stop running. All right, so now if we continue, all right, so the fields in the text are still going. This time we actually got the keyboard because the code is actually running at this point as I type letters they do in fact make it to the label, so sort of a blur, sort of a whirlwind. You will have the code but does the basic idea make sense? Does it all seem familiar based on what you saw in lecture? All right. >> If you could just show how to wire it out real fast with the option key held down? >> Chris Gerber: Yeah, so the question is can we quickly redo the wiring just to see that happen and we can absolutely do that. All right, so let's first bring up the NIB file. I'm going to make this window a little bit smaller so we can see it. There we go. All right, so the easiest way to start doing this and for the moment we don't need the items over here so let's get that out of our way, give us a little more room to work with. Remember, there's the assistant up here. Show the assistant editor which basically lets us put any two files side by side. By default it will try to pick the most appropriate file for you. So in this case it did automatically detect that I want to look at my header file which is good because that is what I want it to. Just for your own edification, if you click on the filename there it will give you other logical choices. You can also go back and instead of automatic you can put any other file side by side so you could go to manual and I could pick any file to have two different things side by side. But in this case this is what we want. We want to see the view and we want to see our header file. So I can come in here and that's what I wanted. All right, so we can come in and delete the ones that I had set up there. All right so we've got the IB outlets for properties and we've got the IB actions for methods. Since we want to go from our code to the UI, I should be able to option click from the text label to the text label and we'll see that wired up text label to file's owner. I can also do option click from UI text field to text field and then I can do option click, sorry, control click from the text field back to [inaudible]. So this one is actually different because I actually want to use it editing changed so I can come in here and wire that up to the method. I actually don't know where the default is so that would actually be interesting if I had instead just gone there it would have chosen editing end which is in fact not what I wanted in this case. So something to be aware of if you-- >> So right now are you using option or control? >> Chris Gerber: I'm using control at the moment. All right, okay, let's go back to the slides here. All right, so property lists. Property lists are going to be very important for what we're doing because the words we give you are in a property list so that's a very important starting point for you but basically property lists are just key value pairs. You've got a way to look it up and what the result is when you look it up. It's stored in XML which you can just edit manually. It's a standard XML file or you can use the plist editor which gives you a little more of a gooey feel to it. It might make it a little easier. Frequently, property lists are used to store settings and that is probably something that will be useful for you as well. You don't have to worry about a lot of this but this is what a property list would actually look like. The first few lines here just say that essentially this is a property list. We don't have to worry about that. Where it gets more interesting is as we get in here. So in this case in my property list happen to have a dictionary that has a key and a string. So this is my key value pair and it's standard XML and you can go from there. Interesting and useful to note is that NS dictionary and NS array have methods to init with contents of file to read in plist files and they also have methods to write out plist files. Also NS bundle has a method called path for resource of type which lets you get to files in your local file system. So obviously we're going to give you the plist file. It has to get on the device and you have to have a way to find it and read it. So these are both going to be very important for you should you be using plists [inaudible] to do your work. We can look at that quickly as well and we'll go back to Xcode and currently this is right in the same application I just did not show it to you yet and I really haven't put much beyond it, much into it but it's as good a chance for us to see what the [inaudible] looks like as well since we haven't covered much of that. There we go. So you remember when I started I had a breakpoint in my code that was causing us problems? That was my breakpoint. In my view did load function I added two commands. I added a new variable file with path that is going to use NS bundle. It's going to use our main bundle which is predefined. It's going to do path for resource. We've got an NS string here called words and of type of plist and this is basically going to say go into your main bundle which is where your application is storing its data. Look for a file called words that is a words.plist file. Once we've got that path built I'm using an NS mutable array to store some words. It's going to allocate that array and I'm just going to use init with contents of file, file of path. So this is essentially going to load my file. Where we can see that is when we hit run we are going to hit a breakpoint. You'll see that file with path did in fact get set to this long thing on my file system but then the other thing you'll see is that words is now an NS mutable array with 233 thousand objects and I could actually start looking at what that looks like. So it went by very quickly. I just did a PO words which is off the screen, sorry about that, and it doesn't want to go and I can't go any higher. All right, trust that this is PO words. PO is short for print object. At any point when you're at a breakpoint you can use PO to print the contents of an object in the output window here. You can also use just straight P if it's not an object. So remember things like intra-traditional data types are not objects but things like NS string, NS dictionary, etcetera are so you can use that in your development as well. And we're in the home stretch here. So as you start thinking about settings you might also want to look at this other object NS user defaults and there are a number of methods here that I think you'll find useful. Register defaults lets you pre-store defaults for your application but then you can use things like integer for key to read an integer, set integer for key to set an integer into these defaults, string for keys, set object for key. You remember you might think there should be set string for key where a string is just an object, it's an NS string so it's an object. And there's plenty more but that's just one more hint to help you work through your project here. That is the end of my slides. Are there any other questions for the walk through of Evil Hangman? Yes. >> [Inaudible question] >> Chris Gerber: So yeah, the question is around, the spec says we should handle backgrounding the application and basically what that means is if you are playing Evil Hangman and either someone hits the home button or uses the task manager to switch to another app or a phone call comes in or whatever happens that interrupts the flow of your application what should happen is the application will move to the background. It will be in a frozen state but when it comes back, when Evil Hangman is run next it should pick up where you left off. So if you had guessed three letters and you were half way through your game it should know you're in the middle of that game and try to continue playing. Certainly if you ran out of memory and the application crashed or something like that the state wouldn't come back but in any case we can reserve state if we want it to do that. >> [Inaudible question] >> Chris Gerber: So the question asked was whether we have to use NS user defaults to save the game state when the application is backgrounded? You do not need to do that. NS user defaults should be used for when you're first launching the application or when you're starting a new game potentially or when you are saving changes to your settings. >> [Inaudible question] >> Chris Gerber: So your question is then when it backgrounds the state will be saved automatically? And the answer to that would be yes. >> [Inaudible question] >> Chris Gerber: The question is can we say that if you leave the game you lose? That sounds very evil but that is not in compliance with our spec. Yes. >> [Inaudible question] >> Chris Gerber: So the question is do we actually have to draw a hangman? You certainly have the option of drawing arms and legs, etcetera but you can also just for example use a count down that says "I have five guesses left." Yes. >> [Inaudible question] >> Chris Gerber: So the question is the application needs to handle low memory warnings by reloading views as needed. So essentially if you were on the MainViewController the FlipsideViewController can go out of memory. It can be discarded which means that when it initializes you might need to reload from NS user defaults to see what the settings are things like that or if you are on the Flipside the state of your screen might not be preserved. You might have to go over to redraw the screen. There are the callbacks are already there for things such as view--I'd have to look up the actual callback that you'll see but there are callbacks that should show up in the template applications. They used to. I don't know if they still do that will let you detect those states and determine what you need to do. But we can look at that a little more when I can look it up. >> [Inaudible question] >> Chris Gerber: Yes, did receive memory warning. >> Is it a smart idea to just use memory [inaudible] >> Chris Gerber: So the question is is it all right to keep using memory willy-nilly until you get a warning or should you be proactive? I would say the best practice would be to be proactive and that you should certainly not just completely abuse memory on the system to make things easy. Yes. >> [Inaudible question] >> Chris Gerber: So the question is will we be using story boards? And that I believe is to the person's discretion. Certainly what we presented so far formally is not using story boards but you are certainly welcome to use them in your app. In this case it's pretty much as simple as checking the box because if we're really just using the two flip sides but if you wanted to change your segue or something like that you can do that much easier through the story boards I would think. So yeah, it's fine to use them or fine to not. >> Chris, I may say something really stupid but the way I understand it is you load the whole dictionary and you define the number of letters of the words. Let's say three letters, okay? So then the guy says okay, I have an E. So then you eliminate all the words--first of all you eliminate all the words don't have three characters. Then you eliminate all the words that have an E? >> Chris Gerber: Not necessarily. Yeah, so the question to summarize was around the equivalence classes and also word blanks. So for word blanks you can most likely very simply trim down to just words of known length and that's fair to describe the others rather quickly. >> [Inaudible question] >> Chris Gerber: Yes. So the question is will there be a slider? There will be a slider. When you set that that will set what the state will be for the next time you start the game. So when the start new game button is hit you can then or when the application very first launches you can load the dictionary and potentially prune down to just words of the currently configured blank. The other part to your previous question was around equivalence classes. If the letter E was chosen you do not automatically discard all the words that have the letter E in them. You look for the group of words that has the most available choices left. So if that word happens to have an E in it but it gives you a much longer list of possible choices that could be a better choice than discarding those that don't have Es. All right, any other questions? [Silence]