Skip to content

Instantly share code, notes, and snippets.

@classicemi
Created April 29, 2015 08:07
Show Gist options
  • Save classicemi/1aa5e102982d59ff036d to your computer and use it in GitHub Desktop.
Save classicemi/1aa5e102982d59ff036d to your computer and use it in GitHub Desktop.
hangman solver
var fs = require('fs'),
request = require('request'),
inquirer = require('inquirer');
var sessionId = '',
playerId = 'username',
options = {
headers: {"Connection": "close"},
url: 'http://www.hangman.com/game/on',
method: 'POST',
json: true,
body: {}
};
// 字典文件
var dict = fs.readFileSync('words.txt', 'utf-8');
// 保存所有单词的数组
var wordArr = dict.split('\r\n');
// 保存符合某个长度的单词
var wordsMatchLength = [];
// 记录字母的出现频率
var letterFrequency = {};
// 上次猜错次数
var wrongNum = 0;
// 记录已猜过的单词
var lettersGuessed = '';
// 备用字母频率列表
var letterArr =
['E', 'T', 'A', 'O', 'I', 'N', 'S', 'H', 'R', 'D', 'L', 'U', 'C', 'M', 'F', 'W', 'Y', 'P', 'V', 'B', 'G', 'K', 'Q', 'J', 'X', 'Z'];
// 根据传入字符串生成对应的正则对象
function generatePattern(word) {
var patternStr = '';
var starNum = 0;
for (var i = 0, len = word.length; i < len; i++) {
if (word[i] == '*') {
starNum = starNum + 1;
} else {
patternStr = patternStr + (starNum ? '\\w{' + starNum + '}' : '') + word[i];
starNum = 0;
}
}
// 修正结尾的星号
patternStr = patternStr + (starNum ? '\\w{' + starNum + '}' : '');
return new RegExp(patternStr, 'i');
}
function filter(word, wrong, letter) {
var l = '', frequency = 0;
// 第一次执行时,筛选出所有符合长度条件的单词
if (!wordsMatchLength.length) {
for (var i = 0, len = wordArr.length; i < len; i++) {
if (wordArr[i].length === word.length) {
wordsMatchLength.push(wordArr[i]);
}
}
}
// 若上次猜错,筛去所有包含上次所猜字母的单词
if (wrong > wrongNum) {
for (var i = 0, len = wordsMatchLength.length; i < len; i++) {
if (wordsMatchLength[i] &&
(wordsMatchLength[i].indexOf(letter.toLowerCase()) > -1 || wordsMatchLength[i].indexOf(letter.toUpperCase()) > -1)) {
wordsMatchLength.splice(i, 1);
i--;
len--;
}
}
// 若上次猜对,对现有模式进行正则匹配,筛选出符合条件的单词
} else {
for (var i = 0, len = wordsMatchLength.length; i < len; i++) {
if (wordsMatchLength[i] && !generatePattern(word).test(wordsMatchLength[i])) {
wordsMatchLength.splice(i, 1);
i--;
len--;
}
}
}
// 筛选完成后,对剩下字母的频率进行统计
for (var i = 0, len = wordsMatchLength.length; i < len; i++) {
for (var j = 0, innerLen = wordsMatchLength[i].length; j < innerLen; j++) {
letterFrequency[wordsMatchLength[i][j].toLowerCase()] == undefined
? letterFrequency[wordsMatchLength[i][j].toLowerCase()] = 1
: letterFrequency[wordsMatchLength[i][j].toLowerCase()]++;
}
}
for (var key in letterFrequency) {
if (letterFrequency[key] > frequency && lettersGuessed.indexOf(key) < 0) {
frequency = letterFrequency[key];
l = key;
}
}
console.log('dict remaining: ' + wordsMatchLength.length + ' words');
// 若单词不在词典中,根据备用频率表进行选择
if (!wordsMatchLength.length) {
for (var i = 0, len = letterArr.length; i < len; i++) {
if (lettersGuessed.indexOf(letterArr[i]) < 0) {
l = letterArr[i];
break;
}
}
}
lettersGuessed = lettersGuessed + l;
wrongNum = wrong;
letterFrequency = {};
return l;
}
function guess(word, wrongNum, letter) {
var letterToGuess = filter(word, wrongNum, letter);
options.body = {
"sessionId": sessionId,
"action": "guessWord",
"guess": letterToGuess.toUpperCase()
};
request(options, function(err, res, data) {
if (err) {
console.log(err);
} else if(data.message) {
console.log(message);
} else {
console.log('your guess: ', letterToGuess.toUpperCase());
console.log('current word: ', data.data.word);
console.log('current word count: ', data.data.totalWordCount);
console.log('wrong guess: ', data.data.wrongGuessCountOfCurrentWord + ' times');
}
setTimeout(function() {
auto(data, letterToGuess);
}, 0);
});
}
function auto(data, letterToGuess) {
if (data == 'start') {
options.body = {
"playerId": playerId,
"action": "startGame"
};
request(options, function(err, res, data) {
if (!err && res.statusCode == 200) {
console.log(data)
console.log('game restarted,your sessionId is: ', data.sessionId);
sessionId = data.sessionId;
setTimeout(function() {
auto(data);
}, 0);
} else {
console.log(err);
}
});
return;
}
// game start
if (data.message && data.message == 'THE GAME IS ON') {
sessionId = data.sessionId;
setTimeout(nextWord, 0);
return;
}
if (data.message && data.message == 'No more word to guess.') {
setTimeout(getResult, 0);
return;
}
// unfinished situation
if (data.data.word.indexOf('*') > -1
&& data.data.wrongGuessCountOfCurrentWord < 10
&& data.data.totalWordCount <= 80) {
setTimeout(function() {
guess(data.data.word, data.data.wrongGuessCountOfCurrentWord, letterToGuess);
}, 0);
} else if (data.data.word.indexOf('*') == -1
|| data.data.wrongGuessCountOfCurrentWord >= 10) { // guess finished
// 猜词完毕后,复原辅助变量
wordsMatchLength = [];
letterFrequency = {};
wrongNum = 0;
lettersGuessed = '';
setTimeout(nextWord, 0);
} else if (data.data.totalWordCount >= 80 && data.data.wrongGuessCountOfCurrentWord >= 10) {
setTimeout(getResult, 0);
}
}
function startGame() {
inquirer.prompt(
[{
type: "input",
name: "startGame",
message: "please enter 'y' to automatically play the game, or enter session id to continue: "
}], function(answers) {
if (answers.startGame.toLowerCase() != 'y') {
sessionId = answers.startGame;
nextWord();
return;
}
setTimeout(function() {
auto('start');
}, 0);
}
);
}
function nextWord() {
options.body = {
"sessionId": sessionId,
"action": "nextWord"
};
request(options, function(err, res, data) {
if (err) {
console.log(err);
} else if(data.message) {
console.log(data.message);
} else {
console.log('current word: ', data.data.word);
console.log('current word count: ', data.data.totalWordCount);
console.log('wrong guess: ', data.data.wrongGuessCountOfCurrentWord + ' times');
index = 0;
}
auto(data);
});
}
function getResult() {
options.body = {
"sessionId": sessionId,
"action": "getResult"
};
function submitDicide() {
inquirer.prompt(
[{
type: "input",
name: "submitDicision",
message: "enter 'y' to submit your score or enter 'n' to restart: "
}], function(answers) {
if (answers.submitDicision.toLowerCase() != 'y' && answers.submitDicision.toLowerCase() != 'n') {
console.log('illegal command, please reenter: ');
submitDicide();
return;
}
switch (answers.submitDicision.toLowerCase()) {
case 'y':
submit();
break;
case 'n':
startGame();
break;
default:
break;
}
}
);
}
request(options, function(err, res, data) {
if (err) {
console.log(err);
} else if(data.message) {
console.log(message);
} else {
console.log(data);
console.log('current word: ', data.data.word);
console.log('current word count: ', data.data.totalWordCount);
console.log('wrong guess: ', data.data.wrongGuessCountOfCurrentWord + ' times');
console.log('current score: ', data.data.score);
submitDicide();
}
});
}
function submit() {
options.body = {
"sessionId": sessionId,
"action": "submitResult"
};
request(options, function(err, res, data) {
if (err) {
console.log(err);
} else {
console.log('player: ', data.data.playerId);
console.log('session id: ', data.data.sessionId);
console.log('total word count: ', data.data.totalWordCount);
console.log('correct word count: ', data.data.correctWordCount);
console.log('total wrong guess count: ', data.data.totalWrongGuessCount);
console.log('total score: ', data.data.score);
console.log('submit time: ', data.data.datetime);
}
});
}
startGame();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment