《C++游戏编程入门》第6章 引用:Tic-Tac-Toe
6.1 使用引用
引用为变量提供另一个名称,指针的语法糖。
06.referencing.cpp
#include <iostream>
using namespace std;
int main()
{
int myScore = 1000;
int &mikesScore = myScore; // 创建引用,声明时必须初始化
cout << "myScore is: " << myScore << "\n";
cout << "mikesScore is: " << mikesScore << "\n\n"; // 访问被引用的值
cout << "Adding 500 to myScore\n";
myScore += 500;
cout << "myScore is: " << myScore << "\n";
cout << "mikesScore is: " << mikesScore << "\n\n";
cout << "Adding 500 to mikesScore\n";
mikesScore += 500; // 修改被引用的值
cout << "myScore is: " << myScore << "\n";
cout << "mikesScore is: " << mikesScore << "\n\n";
return 0;
}
6.2 通过传递引用改变实参
函数传值时无法改变实参,而引用可以。
06.swap.cpp
#include <iostream>
using namespace std;
void badSwap(int x, int y); // 传值不改变实参
void goodSwap(int &x, int &y); // 传引用可以改变实参
int main()
{
int myScore = 150;
int yourScore = 1000;
cout << "Original values\n";
cout << "myScore: " << myScore << "\n";
cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling badSwap()\n";
badSwap(myScore, yourScore);
cout << "myScore: " << myScore << "\n";
cout << "yourScore: " << yourScore << "\n\n";
cout << "Calling goodSwap()\n";
goodSwap(myScore, yourScore);
cout << "myScore: " << myScore << "\n";
cout << "yourScore: " << yourScore << "\n";
return 0;
}
void badSwap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
void goodSwap(int &x, int &y)
{
int temp = x;
x = y;
y = temp;
}
6.3 传递引用以提高效率
值传递,复制,额外开销。
引用效率高,不需要实参复制。
06.inventory_displayer.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// parameter vec is a constant reference to a vector of strings
void display(const vector<string> &inventory); // 常量引用无法修改
int main()
{
vector<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
display(inventory);
return 0;
}
// parameter vec is a constant reference to a vector of strings
void display(const vector<string> &vec)
{
cout << "Your items:\n";
for (vector<string>::const_iterator iter = vec.begin();
iter != vec.end(); ++iter)
{
cout << *iter << endl;
}
}
6.5 返回引用
返回对象时开销可能较大,可返回引用。
06.inventory_referencer.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// returns a reference to a string
string &refToElement(vector<string> &inventory, int i);
int main()
{
vector<string> inventory;
inventory.push_back("sword");
inventory.push_back("armor");
inventory.push_back("shield");
// displays string that the returned reference refers to
cout << "Sending the returned reference to cout:\n";
cout << refToElement(inventory, 0) << "\n\n";
// assigns one reference to another -- inexpensive assignment
cout << "Assigning the returned reference to another reference.\n";
string &rStr = refToElement(inventory, 1);
cout << "Sending the new reference to cout:\n";
cout << rStr << "\n\n";
// copies a string object -- expensive assignment
cout << "Assigning the returned reference to a string object.\n";
string str = refToElement(inventory, 2);
cout << "Sending the new string object to cout:\n";
cout << str << "\n\n";
// altering the string object through a returned reference
cout << "Altering an object through a returned reference.\n";
rStr = "Healing Potion";
cout << "Sending the altered object to cout:\n";
cout << inventory[1] << endl;
return 0;
}
// returns a reference to a string
string &refToElement(vector<string> &vec, int i)
{
return vec[i];
}
//返回引用不能指向超出作用域范围的对象(函数返回时,对象已不存在)
string &badReference()
{
string local = "This string will cease to exist once the function ends.";
return local;
}
6.6 Tic-Tac-Toc游戏简介
06.tic-tac-toe.cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
// global constants
const char X = 'X';
const char O = 'O';
const char EMPTY = ' ';
const char TIE = 'T';
const char NO_ONE = 'N';
// function prototypes
void instructions();
char askYesNo(string question);
int askNumber(string question, int high, int low = 0);
char humanPiece();
char opponent(char piece);
void displayBoard(const vector<char> &board);
char winner(const vector<char> &board);
bool isLegal(const vector<char> &board, int move);
int humanMove(const vector<char> &board, char human);
int computerMove(vector<char> board, char computer);
void announceWinner(char winner, char computer, char human);
// main function
int main()
{
int move;
const int NUM_SQUARES = 9;
vector<char> board(NUM_SQUARES, EMPTY); // 空棋盘
instructions(); // 规则介绍
char human = humanPiece(); // 人类选择先后手
char computer = opponent(human); // 计算机取对手
char turn = X; // X先手
displayBoard(board); // 显示棋盘
while (winner(board) == NO_ONE) // 未分胜负
{
// 当前轮次
if (turn == human)
{
move = humanMove(board, human); // 人类移动
board[move] = human; // 下棋
}
else
{
move = computerMove(board, computer); // 计算机移动
board[move] = computer; // 下棋
}
displayBoard(board); // 显示棋盘
turn = opponent(turn); // 轮次交替
}
announceWinner(winner(board), computer, human); // 宣布获胜情况
return 0;
}
// 显示游戏操作指南
void instructions()
{
cout << "Welcome to the ultimate man-machine showdown: Tic-Tac-Toe.\n";
cout << "--where human brain is pit against silicon processor\n\n";
cout << "Make your move known by entering a number, 0 - 8. The number\n";
cout << "corresponds to the desired board position, as illustrated:\n\n";
cout << " 0 | 1 | 2\n";
cout << " ---------\n";
cout << " 3 | 4 | 5\n";
cout << " ---------\n";
cout << " 6 | 7 | 8\n\n";
cout << "Prepare yourself, human. The battle is about to begin.\n\n";
}
// 一直询问,直到输入y或n
char askYesNo(string question)
{
char response;
do
{
cout << question << " (y/n): ";
cin >> response;
} while (response != 'y' && response != 'n');
return response;
}
// 一直询问,直到输入范围内的数值
int askNumber(string question, int high, int low)
{
int number;
do
{
cout << question << " (" << low << " - " << high << "): ";
cin >> number;
} while (number > high || number < low);
return number;
}
// 是否先手
char humanPiece()
{
char go_first = askYesNo("Do you require the first move?");
if (go_first == 'y')
{
cout << "\nThen take the first move. You will need it.\n";
return X;
}
else
{
cout << "\nYour bravery will be your undoing... I will go first.\n";
return O;
}
}
// 返回对手棋子
char opponent(char piece)
{
if (piece == X)
{
return O;
}
else
{
return X;
}
}
// 显示棋盘
void displayBoard(const vector<char> &board)
{
cout << "\n\t" << board[0] << " | " << board[1] << " | " << board[2];
cout << "\n\t"
<< "---------";
cout << "\n\t" << board[3] << " | " << board[4] << " | " << board[5];
cout << "\n\t"
<< "---------";
cout << "\n\t" << board[6] << " | " << board[7] << " | " << board[8];
cout << "\n\n";
}
// 返回获胜情况
// 玩家获胜,X或O
// 方格已填满且无人获胜,T
// 方格未填满且无人获胜,N
char winner(const vector<char> &board)
{
// 8种获胜情况
const int WINNING_ROWS[8][3] = {{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
{0, 3, 6},
{1, 4, 7},
{2, 5, 8},
{0, 4, 8},
{2, 4, 6}};
const int TOTAL_ROWS = 8;
// if any winning row has three values that are the same (and not EMPTY),
// then we have a winner
for (int row = 0; row < TOTAL_ROWS; ++row)
{
// 非空且三个棋子相同,玩家获胜
if ((board[WINNING_ROWS[row][0]] != EMPTY) &&
(board[WINNING_ROWS[row][0]] == board[WINNING_ROWS[row][1]]) &&
(board[WINNING_ROWS[row][1]] == board[WINNING_ROWS[row][2]]))
{
return board[WINNING_ROWS[row][0]];
}
}
// since nobody has won, check for a tie (no empty squares left)
// 棋盘填满,平局
if (count(board.begin(), board.end(), EMPTY) == 0)
return TIE;
// since nobody has won and it isn't a tie, the game ain't over
// 未分胜负
return NO_ONE;
}
// 棋盘上某个位置能否下棋(是否为空的)
inline bool isLegal(int move, const vector<char> &board)
{
return (board[move] == EMPTY);
}
// 询问人类玩家输入方格号,直到合理且空位
int humanMove(const vector<char> &board, char human)
{
int move = askNumber("Where will you move?", (board.size() - 1));
while (!isLegal(move, board))
{
cout << "\nThat square is already occupied, foolish human.\n";
move = askNumber("Where will you move?", (board.size() - 1));
}
cout << "Fine...\n";
return move;
}
// 计算机获取最优下棋位置
int computerMove(vector<char> board, char computer)
{
const unsigned NUM = board.size();
// 遍历查找计算机能一步获胜的方格位置
for (unsigned move = 0; move < NUM; move++)
{
if (isLegal(move, board)) // 当前位置为空
{
// 尝试移动
board[move] = computer;
// 测试计算机能否获胜
if (winner(board) == computer)
return move;
// 撤销移动
board[move] = EMPTY;
}
}
// 遍历查找人类能一步获胜的方格位置
char human = opponent(computer);
for (unsigned move = 0; move < NUM; move++)
{
if (isLegal(move, board)) // 当前位置为空
{
// 尝试移动
board[move] = human;
// 测试人类能否获胜
if (winner(board) == human)
return move;
// 撤销移动
board[move] = EMPTY;
}
}
// 中心》四边》四角
const int BEST_MOVES[NUM] = {4, 0, 2, 6, 8, 1, 3, 5, 7};
for (unsigned move = 0; move < NUM; move++)
if (isLegal(BEST_MOVES[move], board))
return move;
return -1; // 不可能
}
// 宣布获胜情况
void announceWinner(char winner, char computer, char human)
{
if (winner == computer)
{
cout << winner << "'s won!\n";
cout << "As I predicted, human, I am triumphant once more -- proof\n";
cout << "that computers are superior to humans in all regards.\n";
}
else if (winner == human)
{
cout << winner << "'s won!\n";
cout << "No, no! It cannot be! Somehow you tricked me, human.\n";
cout << "But never again! I, the computer, so swear it!\n";
}
else
{
cout << "It's a tie.\n";
cout << "You were most lucky, human, and somehow managed to tie me.\n";
cout << "Celebrate... for this is the best you will ever achieve.\n";
}
}