Let's play Hangman?

Python3, 299 Bytes

import sys,collections as c;x,X,N,*Z=sys.argv;print([x for x in c.Counter(''.join([''.join(x)for x in map(set,filter(lambda l:len(l)==int(X)+1and all(x not in X.lower()for x in l)and all(l[int(x[0])-1]==x[1].lower()for x in Z),open('wordlist.txt')))]))if x not in ''.join(Z).lower()and x!='\n'][0])

pretty sure this can be golfed further.

Filters the wordlist for potential matches, builds a char frequency map, and selects the most often occuring character which is not yet picked.


PowerShell, 248 246 241 bytes

$a=$args;$c=$a[0];$b=$a[2..$c];((gc wordlist.txt)-match"^$((1..$c|%{$e=,"[^$($a[1])]"*$c}{$e[$_-1]=(((@($b)-match"^$_\D")[0]-split$_)[-1],$e[$_-1]-ne'')[0]}{$e-join''}))$"-join''-split'\B'|?{!(@($b)-match$_).Count}|group|sort count)[-1].Name

Ungolfed

Well, as much as I could without changing the way it works:

$a=$args
$c=$a[0]
$b=$a[2..$c]
(
    (gc wordlist.txt) -match "^$(
        (
            1..$c | ForEach-Object -Begin {
                $e = ,"[^$($a[1])]" * $c
            } -Process {
                $e[$_-1] = (
                    ( ( @($b) -match "^$_\D" )[0] -split $_ )[-1] , $e[$_-1] -ne ''
                )[0]
            } -End {
                $e-join''
            }
        )
    )$" -join '' -split'\B' |
    Where-Object {
        -not (@($b) -match $_).Count
    } | 
    Group-Object |
    Sort-Object count
)[-1].Name

Breakdown

The approach I took here was to first generate a regular expression to get possible words out of the word list. Since I know the length of the word, and the letters that didn't work, I can make a regex out of that fairly easily.

So in the PEOPLE example, 6 letters with XBZ not being part of the word, I would look to generate ^PE[^XBZ]P[^XBZ]E$.

I'm exploiting the fact that Get-Content (gc) returns an array of lines, and the -match operator when used with an array on the left side, returns an array of matches instead of a bool, so I can quickly get a list of just words that are candidates, once I have the regex.

To generate the regex, I start with an array ($e) of the negative matching character class with $c elements ($c being the number of letters in the word). Iterating through the numbers 1 through $c, I check for a matched letter at that position, and if it exists, I replace the element in $e with that letter.

Once I've iterated through all of the positions, the final array is -joined (with empty string) and we have our regex.

So now I've got an array of all the possible words it could be. A quick -join with empty string on that, gives me one big concatenated string of all the words, the I split on \B (not a word boundary, if I split on empty string I will get 2 extra blank elements), so now I have an array of every letter in every possible word.

Piping that into Where-Object lets me filter out the letters that have already been matched. This part was a real pain. It had to deal with the list of matched letters (which include the position) being 1 element, more than 1 element, or 0 elements, hence forcing $b into an array first so that -match can operate on all them, but that (unfortunately in this case) returns an array, so we have to check .Count. Using !(thing).Count is a bit smaller than using (thing).Count-gt0.

Moving on, now we've got an array of all the single characters (as strings not as chars) from all of the words it could possibly be, minus the letters that were already guessed correctly.

Piping that into Group-Object gives me an object with the counts of each letter, so a quick pipe into Sort-Object count makes it easy to get the highest count. Rather than do (thing|sort count -des)[0] we can use (thing|sort count)[-1]. In PowerShell [-1] gets the last element. At this point we're still dealing with the objects that came from Group-Object so we get the .Name property which is the letter that appears the most.

Notes

  • Should work with PowerShell v3+; almost certainly will choke on 2.
  • Remember when you call a PowerShell script, pass arguments with spaces, not commas.
  • Although I didn't see it in the rules, it looks like everyone is using the filename wordlist.txt otherwise that could shave a few bytes off.
  • Speed shouldn't be a problem. This appears to run instantly for me. The slowest run I could get it to do (.\hangman.ps1 7 0) runs in about 350ms.

Java, 646 640 631 607 606 (short) 790 789 779 (fast) bytes

SHORT

import java.util.*;class I{public static void main(String[]a)throws Exception{char[]w=a[1].toCharArray(),p,q;int l=Integer.parseInt(a[0]),i,z=w.length,j;q=new char[l];for(i=2;i<a.length;i++)q[Character.getNumericValue(a[i].charAt(0))-1]=(char)(a[i].charAt(1)+32);java.io.File u=new java.io.File("wordlist.txt");Scanner s=new Scanner(u);while(s.hasNextLine()){p=s.nextLine().toCharArray();if(p.length==l)for(i=0;i<l;i++)if(p[i]==q[i]||q[i]=='\0'){if(i==l-1)y:for(i=0;i<l;i++)for(j=0;j<z;j++)if(!(p[i]==w[j])){if(j==z-1){System.out.print(p[new String(q).indexOf('\0')]);return;}}else break y;}else{break;}}}}

FAST

import java.util.*;class I{public static void main(String[]a)throws Exception{char[]w=a[1].toCharArray(),p,q;int l=Integer.parseInt(a[0]),i,z=w.length,j,k,o,n[]=new int[255],r[];q=new char[l];for(i=2;i<a.length;i++)q[Character.getNumericValue(a[i].charAt(0))-1]=(char)(a[i].charAt(1)+32);String m=new String(q);java.io.File u=new java.io.File("wordlist.txt");Scanner s=new Scanner(u);while(s.hasNextLine()){p=s.nextLine().toCharArray();h:if(p.length==l)for(i=0;i<l;i++)if(p[i]==q[i]||q[i]=='\0'){if(i==l-1)y:for(i=0;i<l;i++)for(j=0;j<z;j++)if(p[i]!=w[j]){if(j==z-1){for(k=0;k<l-m.replace("\0","").length();k++)n[(int)p[new String(q).indexOf('\0',k)]]++;break h;}}else break y;}else{break;}}r=n.clone();Arrays.sort(n);for(o=0;o<255;o++)System.out.print(r[o]==n[254]?(char)o:"");}}

Put the wordlist file in the folder.

Short version algorithm

  1. Load Args
  2. Construct the word we are trying to guess {'p','e','\0','p','\0','e'}
  3. Load WordList
  4. Go through each line of WordList
  5. Stop when you find that the whole word matches this condition p[i] == q[i] || q[i] == '\0' where p is a word from the wordlist (char array), and q is the word we are trying to guess
  6. Loop through wrong chars and compare to the word
  7. Print the first missing character

Long version algorithm

  1. Short steps 1-7
  2. Increment the character count in the n array for the missing chars
  3. Loop until all the words come through
  4. Print the character that had the highest count