}
}
bQuit = TRUE;
}
}
}
CXvsOv1View::VinControl();
CView::OnLButtonDown(nFlags, point);
}
Для прорисовки крестиков и ноликов используется две функции класса view - DrawX и DrawO. В процессе рисования они используют функции вывода класса CDC. Сначала DrawX создает инструмент для рисования - красный карандаш (pen) для линий толщиной 10 условных единиц (для типа отображения MM_LOENGLISH одна условная единица эквивалентна 0,1 логического дюйма):
CPen pen (PS_SOLID, 10, RGB(255, 0, 0));
Затем с помощью следующих предложений рисуются две пересекающиеся линии:
pDC->MoveTo (rect.left, rect.top);pDC->LineTo (rect.right, rect.bottom);pDC->MoveTo (rect.left, rect.bottom);pDC->LineTo (rect.right, rect.top);Аналогичным образом функция DrawO создает свой инструмент для рисования - карандаш синего цвета для линий толщиной 10 условных единиц:
CPen pen (PS_SOLID, 10, RGB(0, 0, 255));и рисует O с помощью MFC-функции CDC::Ellipse
pDC->Ellipse (rect);В обоих случаях до начала каких-либо операций рисования нужный карандаш выбирается в контекст устройства с помощью функции CDC::SelectObject и возвращается обратно по завершении работ с ним:
CPen* pOldPen = pDC->SelectObject (&pen);...pDC->SelectObject (pOldPen);Чтобы внутренняя область значка О не закрашивалась, в view-функции DrawO перед обращением к CDC::Ellipse выбирается в контекст NULL-кисть - не заполняющая область рисования:
pDC->SelectStockObject (NULL_BRUSH); Функции DrawX и DrawO выглядят следующим образом: void CXvsOv1View::DrawX(CDC *pDC, CRect *pRect){// Скопировать параметры переданного прямоугольника и// уменьшить его размеры.//CRect rect;rect.CopyRect (pRect);rect.DeflateRect (10, 10); //// Создать красный карандаш и нарисовать им Х.//CPen pen (PS_SOLID, 10, RGB (255, 0, 0));//нужный карандаш выбирается в контекст устройства с помощью функции CDC::SelectObjectCPen* pOldPen = pDC->SelectObject (&pen);pDC->MoveTo (rect.left, rect.top); //pDC->LineTo (rect.right, rect.bottom); //pDC->MoveTo (rect.left, rect.bottom); //рисуются две пересекающиеся линииpDC->LineTo (rect.right, rect.top); ////карандаш возвращается обратно по завершении работ с нимpDC->SelectObject (pOldPen); } void CXvsOv1View::Draw0(CDC *pDC, CRect *pRect){// Скопировать параметры переданного прямоугольника и// уменьшить его размеры.//CRect rect;rect.CopyRect (pRect);rect.DeflateRect (10, 10); //// Создать синий карандаш и нарисовать им 0.//CPen pen (PS_SOLID, 10, RGB (0, 0, 255));CPen* pOldPen = pDC->SelectObject (&pen);pDC->SelectStockObject (NULL_BRUSH); pDC->Ellipse (rect);//рисуется эллипс//карандаш возвращается обратно по завершении работ с нимpDC->SelectObject (pOldPen); } Функция VinControl()осуществляет проверку, выстроены ли в ряд 4 крестика или нолика и выводит соответствующие сообщения при выполнении этих условий. Функция содержит 56 циклов, при выполнении каждого из которого выводится соответствующее сообщение: "Крестики выстроили ряд!" или "Нолики выстроили ряд!". Функция VinControl()выглядит следующим образом: void CXvsOv1View::VinControl(){ CXvsOv1Doc* pDoc = GetDocument (); // Получаем указатель на класс document://Проверка победы крестиков по горизонталямif ((pDoc->m_grid[0][0] == 1) && (pDoc->m_grid[1][0] == 1) && (pDoc->m_grid[2][0] == 1) && (pDoc->m_grid[3][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][0] == 1) && (pDoc->m_grid[2][0] == 1) && (pDoc->m_grid[3][0] == 1) && (pDoc->m_grid[4][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][1] == 1) && (pDoc->m_grid[1][1] == 1) && (pDoc->m_grid[2][1] == 1) && (pDoc->m_grid[3][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][1] == 1) && (pDoc->m_grid[2][1] == 1) && (pDoc->m_grid[3][1] == 1) && (pDoc->m_grid[4][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][2] == 1) && (pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][2] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][2] == 1) && (pDoc->m_grid[4][2] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][3] == 1) && (pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[3][3] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[3][3] == 1) && (pDoc->m_grid[4][3] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][4] == 1) && (pDoc->m_grid[1][4] == 1) && (pDoc->m_grid[2][4] == 1) && (pDoc->m_grid[3][4] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][4] == 1) && (pDoc->m_grid[2][4] == 1) && (pDoc->m_grid[3][4] == 1) && (pDoc->m_grid[4][4] == 1))AfxMessageBox("Крестики выстроили ряд!"); //Проверка победы крестиков по вертикалямif ((pDoc->m_grid[0][4] == 1) && (pDoc->m_grid[0][3] == 1) && (pDoc->m_grid[0][2] == 1) && (pDoc->m_grid[0][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][3] == 1) && (pDoc->m_grid[0][2] == 1) && (pDoc->m_grid[0][1] == 1) && (pDoc->m_grid[0][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][4] == 1) && (pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[1][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[1][1] == 1) && (pDoc->m_grid[1][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[2][4] == 1) && (pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[2][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[2][1] == 1) && (pDoc->m_grid[2][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[3][4] == 1) && (pDoc->m_grid[3][3] == 1) && (pDoc->m_grid[3][2] == 1) && (pDoc->m_grid[3][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[3][3] == 1) && (pDoc->m_grid[3][2] == 1) && (pDoc->m_grid[3][1] == 1) && (pDoc->m_grid[3][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[4][4] == 1) && (pDoc->m_grid[4][3] == 1) && (pDoc->m_grid[4][2] == 1) && (pDoc->m_grid[4][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[4][3] == 1) && (pDoc->m_grid[4][2] == 1) && (pDoc->m_grid[4][1] == 1) && (pDoc->m_grid[4][0] == 1))AfxMessageBox("Крестики выстроили ряд!"); //Проверка победы крестиков по диагоналям снизу слева - вверх вправоif ((pDoc->m_grid[0][3] == 1) && (pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[2][1] == 1) && (pDoc->m_grid[3][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][4] == 1) && (pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][1] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][3] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][1] == 1) && (pDoc->m_grid[4][0] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][4] == 1) && (pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[3][2] == 1) && (pDoc->m_grid[4][1] == 1))AfxMessageBox("Крестики выстроили ряд!"); //Проверка победы крестиков по диагоналям снизу справа - вверх влевоif ((pDoc->m_grid[1][0] == 1) && (pDoc->m_grid[2][1] == 1) && (pDoc->m_grid[3][2] == 1) && (pDoc->m_grid[4][3] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][0] == 1) && (pDoc->m_grid[1][1] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][3] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[1][1] == 1) && (pDoc->m_grid[2][2] == 1) && (pDoc->m_grid[3][3] == 1) && (pDoc->m_grid[4][4] == 1))AfxMessageBox("Крестики выстроили ряд!");if ((pDoc->m_grid[0][1] == 1) && (pDoc->m_grid[1][2] == 1) && (pDoc->m_grid[2][3] == 1) && (pDoc->m_grid[3][4] == 1))AfxMessageBox("Крестики выстроили ряд!"); //--------------------------------------------------------------------------------- //Проверка победы ноликов по горизонталямif ((pDoc->m_grid[0][0] == 2) && (pDoc->m_grid[1][0] == 2) && (pDoc->m_grid[2][0] == 2) && (pDoc->m_grid[3][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][0] == 2) && (pDoc->m_grid[2][0] == 2) && (pDoc->m_grid[3][0] == 2) && (pDoc->m_grid[4][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][1] == 2) && (pDoc->m_grid[1][1] == 2) && (pDoc->m_grid[2][1] == 2) && (pDoc->m_grid[3][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][1] == 2) && (pDoc->m_grid[2][1] == 2) && (pDoc->m_grid[3][1] == 2) && (pDoc->m_grid[4][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][2] == 2) && (pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][2] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][2] == 2) && (pDoc->m_grid[4][2] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][3] == 2) && (pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[3][3] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[3][3] == 2) && (pDoc->m_grid[4][3] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][4] == 2) && (pDoc->m_grid[1][4] == 2) && (pDoc->m_grid[2][4] == 2) && (pDoc->m_grid[3][4] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][4] == 2) && (pDoc->m_grid[2][4] == 2) && (pDoc->m_grid[3][4] == 2) && (pDoc->m_grid[4][4] == 2))AfxMessageBox("Нолики выстроили ряд!"); //Проверка победы крестиков по вертикалямif ((pDoc->m_grid[0][4] == 2) && (pDoc->m_grid[0][3] == 2) && (pDoc->m_grid[0][2] == 2) && (pDoc->m_grid[0][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][3] == 2) && (pDoc->m_grid[0][2] == 2) && (pDoc->m_grid[0][1] == 2) && (pDoc->m_grid[0][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][4] == 2) && (pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[1][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[1][1] == 2) && (pDoc->m_grid[1][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[2][4] == 2) && (pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[2][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[2][1] == 2) && (pDoc->m_grid[2][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[3][4] == 2) && (pDoc->m_grid[3][3] == 2) && (pDoc->m_grid[3][2] == 2) && (pDoc->m_grid[3][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[3][3] == 2) && (pDoc->m_grid[3][2] == 2) && (pDoc->m_grid[3][1] == 2) && (pDoc->m_grid[3][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[4][4] == 2) && (pDoc->m_grid[4][3] == 2) && (pDoc->m_grid[4][2] == 2) && (pDoc->m_grid[4][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[4][3] == 2) && (pDoc->m_grid[4][2] == 2) && (pDoc->m_grid[4][1] == 2) && (pDoc->m_grid[4][0] == 2))AfxMessageBox("Нолики выстроили ряд!"); //Проверка победы крестиков по диагоналям снизу слева - вверх вправоif ((pDoc->m_grid[0][3] == 2) && (pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[2][1] == 2) && (pDoc->m_grid[3][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][4] == 2) && (pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][1] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][3] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][1] == 2) && (pDoc->m_grid[4][0] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][4] == 2) && (pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[3][2] == 2) && (pDoc->m_grid[4][1] == 2))AfxMessageBox("Нолики выстроили ряд!"); //Проверка победы крестиков по диагоналям снизу справа - вверх влевоif ((pDoc->m_grid[1][0] == 2) && (pDoc->m_grid[2][1] == 2) && (pDoc->m_grid[3][2] == 2) && (pDoc->m_grid[4][3] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][0] == 2) && (pDoc->m_grid[1][1] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][3] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[1][1] == 2) && (pDoc->m_grid[2][2] == 2) && (pDoc->m_grid[3][3] == 2) && (pDoc->m_grid[4][4] == 2))AfxMessageBox("Нолики выстроили ряд!");if ((pDoc->m_grid[0][1] == 2) && (pDoc->m_grid[1][2] == 2) && (pDoc->m_grid[2][3] == 2) && (pDoc->m_grid[3][4] == 2))AfxMessageBox("Нолики выстроили ряд!");} Теперь нужно рассказать о задаче, связанной с хранением данных о крестиках и ноликах, что возлагается на класс document. В нем существует принадлежащая переменная m_grid, представляющая собой массив 5х5, состоящий из 1-байт элементов. Каждый элемент этого массива соответствует одной клетке игрового поля. Для начала всем элементам присваиваются нулевые значения, чтобы все клетки оказались пустыми. При вводе X или O надлежащему элементу присваивается соответственно значение 1 или 2. Чтобы исключить возможность прямого обращения из класса view к массиву m_grid, добавлены две принадлежащие public-функции - AddX и AddO, которые вносят в этот массив сведения о крестиках и ноликах. Инициализация массива m_grid и обнуление его элементов происходит в функции OnNewDocument, то есть при создании каждого нового документа (при каждом начале новой игры). Итак вид функции OnNewDocument (о назначении переменной m_bXsTurn будет сказано позднее): BOOL CXvsOv1Doc::OnNewDocument(){if (!CDocument::OnNewDocument())return FALSE; for (int i=0; i<5; i++)for (int j=0; j<5; j++)m_grid[i][j]=0;// TODO: add reinitialization code here// (SDI documents will reuse this document) m_bXsTurn=TRUE;return TRUE;} Для внесения сведений о крестиках и ноликах в массив m_grid используются принадлежащие ему две public-функции AddX и AddO. Функция AddX присваивает соответствующему элементу массива 1, а функция AddO – 2.Однако прежде чем выбранная клетка будет заполнена, необходимо, чтобы в классе view выполнялась проверка, пуста ли данная клетка. Для этого в классе document существует принадлежащая public-функция с именем GetSquare, которая считывает содержащиеся в клетке с заданными координатами данные. Если в функцию GetSquare передаются разрешенные номера строки и столбца, она передает 0, если указанная клетка пуста; 1, если в ней находится X; или 2, если в ней O. Если ее return-значение -1, значит, был указан недопустимый номер строки или столбца.Для отслеживания очередности существует принадлежащая переменная m_bXsTurn; когда ход X, ее значение устанавливается равным TRUE, когда ход O - FALSE. Поскольку m_bXsTurn, как и m_grid, относится к данным программы, то она относится к классу document. Ее начальное значение TRUE будет задаваться в CXvsOv1Doc::OnNewDocument; если в document добавляется X, ей присваиваевается значение FALSE, а если O – TRUE.Итак функции OnNewDocument, AddX, AddO и IsItXsTurn .выглядят следующим образом (о назначении выставления флажка изменений SetModifiedFlag() сказано ниже) : void CXvsOv1Doc::AddX(int i, int j) //Это - принадлежащая массиву m_grid public-функция, которая будет вносить в него//сведения о крестиках и ноликах.{if ((i >= 0) && (i <= 4) && (j >= 0) && (j <= 4))m_grid[i][j]=1; //Добавление X в массивSetModifiedFlag (); // Выставить флажок изменений// в классе documentm_bXsTurn=FALSE;// переменной m_bXsTurn будет присваиваться значение FALSE, если в массив// m_grid вносится X.//Далее следует O } void CXvsOv1Doc::AddO(int i, int j) //Это - принадлежащая массиву m_grid public-функция, которая будет вносить в него//сведения о крестиках и ноликах.{if ((i >= 0) && (i <= 4) && (j >= 0) && (j <= 4))m_grid[i][j]=2; //Добавление O в массив SetModifiedFlag (); // Выставить флажок изменений// в классе documentm_bXsTurn=TRUE; //переменной m_bXsTurn будет присваиваться значение TRUE, если в массив//m_grid вносится O.//Далее следует X} BYTE CXvsOv1Doc::GetSquare(int i, int j){if ((i >= 0) && (i <= 4) && (j >= 0) && (j <= 4))return m_grid[i][j];return (BYTE) -1; //если в функцию GetSquare передаются разрешенные номера строки и столбца,//она передает 0, если указанная клетка пуста; 1, если в ней находится X; или 2, если в ней O. //Если return-значение ф-ции GetSquare равно -1, значит,//был указан недопустимый номер строки или столбца.return m_bXsTurn; } BYTE CXvsOv1Doc::IsItXsTurn() //путем обращений к функции IsItXsTurn класса document,//объект класса View может получить сведения о том, чей ход.//Полученное от нее ненулевое значение подразумевает очередь за X; а 0 - следующий ход O. {return m_bXsTurn;} Таким образом, последнее на что стоит обратить внимание в рамках данного пункта является задача о возможности сохранения и загрузки начатых игр. Для ее решения необходимо внести изменения в функцию Serialize(), принадлежащую уже созданному классу document, и добавить к ней операторы пересылки рабочих данных в объект CArchive или из него. Далее показано как будет выглядеть функция Serialize() класса document после внесения изменений, предназначенных для передачи переменных CXvsOv1Doc::m_grid и C XvsOv1Doc::m_bXsTurn в архив и обратно. Значения, содержащиеся в массиве m_grid, пересылаются последовательно один за другим при выполнении вложенного цикла for; затем сразу же обрабатывается переменная m_bXsTurn. Не имеет принципиального значения, какие данные переправляются в первую очередь, надо только соблюдать порядок. Если в архив отправляется сначала m_bXsTurn, а затем m_grid, тогда чтение из архива следует производить в аналогичном порядке. void CXvsOv1Doc::Serialize(CArchive& ar){if (ar.IsStoring()){for (int i=0; i<5; i++)for (int j=0; j<5; j++)ar << m_grid[i][j];ar << m_bXsTurn;}else{for (int i=0; i<5; i++)for (int j=0; j<5; j++)ar >> m_grid[i][j];ar >> m_bXsTurn; }}Чтобы исключить вероятность случайных потерь рабочих данных, корректно составленные программы должны предусматривать средства выдачи пользователю сообщений о необходимости сохранения еще не записанных данных, если программа получает команду завершить работу. В программах архитектуры document/view за решение этой небольшой задачи отвечают средства MFC, но только при том условии, если имеется информация о наличии в рабочем документе еще не сохраненных данных.