BlinkenArea - GitList
Repositories
Blog
Wiki
Blinker
Code
Commits
Branches
Tags
Search
Tree:
ae99ab3
Branches
Tags
master
Blinker
src
common
Pong.cpp
implement simple, fast computer players
Stefan Schuermans
commited
ae99ab3
at 2019-06-10 18:25:59
Pong.cpp
Blame
History
Raw
/* Blinker Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org> Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html a blinkenarea.org project */ #include <stdlib.h> #include <string> #include <vector> #include <BlinkenLib/BlinkenFrame.h> #include "File.h" #include "Format.h" #include "FormatFile.h" #include "Game.h" #include "Mgrs.h" #include "Module.h" #include "OutStreamFile.h" #include "Pong.h" #include "Time.h" #include "TimeCallee.h" namespace Blinker { /** * @brief constructor * @param[in] name module name * @param[in] mgrs managers * @param[in] dirBase base directory */ Pong::Pong(const std::string &name, Mgrs &mgrs, const Directory &dirBase): Game(name, mgrs, dirBase), m_fileBallColor(dirBase.getFile("ballColor")), m_fileLineColor(dirBase.getFile("lineColor")), m_filePadColor(dirBase.getFile("padColor")), m_ballColor(), m_lineColor(), m_padColor(), m_ballPosX(-1), m_ballPosY(-1), m_ballDirX(0), m_ballDirY(0), m_padSize(0), m_leftPosY(0), m_rightPosY(0) { // FIXME: activate at begin for initial development only activate(); } /// virtual destructor Pong::~Pong() { // cancel time callback request m_mgrs.m_callMgr.cancelTimeCall(this); } /// check for update of configuration (derived game), return true on update bool Pong::updateConfigGame() { bool ret = false; // color file was modified -> convert color, return true for update if (colorUpdate(m_fileBallColor, m_ballColor) || colorUpdate(m_fileLineColor, m_lineColor) || colorUpdate(m_filePadColor, m_padColor)) { ret = true; } return ret; } /// re-initialize game (e.g. due to config change) void Pong::reinitialize() { // compute parameters m_padSize = (m_height + 1) / 3; m_leftPosY = (m_height - m_padSize) / 2; m_rightPosY = (m_height - m_padSize) / 2; // convert colors color2data(m_fileBallColor, m_ballColor); color2data(m_fileLineColor, m_lineColor); color2data(m_filePadColor, m_padColor); // FIXME: start ball for development startBall(); } /// redraw current game image, expected to call sendFrame() at end void Pong::redraw() { int y, x; // draw background rectFill(0, m_height, 0, m_width, m_backgroundColor); // draw middle line: single line on odd width, two dashed lines at even width for (y = 0; y < m_height; ++y) { x = (m_width - (y & 1)) / 2; pixel(y, x, m_lineColor); } // draw pads lineVert(m_leftPosY, m_leftPosY + m_padSize - 1, 0, m_padColor); lineVert(m_rightPosY, m_rightPosY + m_padSize - 1, m_width - 1, m_padColor); // draw ball pixel(m_ballPosY, m_ballPosX, m_ballColor); // send updated image buffer as frame sendFrame(); } /// callback when requested time reached void Pong::timeCall() { // computer player: move pads computerLeft(); computerRight(); // bounce ball bounceBall(); // move ball m_ballPosX += m_ballDirX; m_ballPosY += m_ballDirY; // draw and send frame redraw(); // request next call if needed planTimeCall(); } /// computer player for left pad void Pong::computerLeft() { // ball not moving towards pad: do not move if (m_ballDirX >= 0) { return; } // compute expected ball position at pad int expectedY = m_ballPosY + (m_ballPosX - 1) * m_ballDirY; // compute pad position to hit ball with center of pad int padY = expectedY - m_padSize / 2; // do not move pad out of field if (padY < 0) { padY = 0; } if (padY > m_height - m_padSize) { padY = m_height - m_padSize; } // move pad if (m_leftPosY < padY) { ++m_leftPosY; } else if (m_leftPosY > padY) { --m_leftPosY; } } /// computer player for right pad void Pong::computerRight() { // ball not moving towards pad: do not move if (m_ballDirX <= 0) { return; } // compute expected ball position at pad int expectedY = m_ballPosY + (m_width - 2 - m_ballPosX) * m_ballDirY; // compute pad position to hit ball with center of pad int padY = expectedY - m_padSize / 2; // do not move pad out of field if (padY < 0) { padY = 0; } if (padY > m_height - m_padSize) { padY = m_height - m_padSize; } // move pad if (m_rightPosY < padY) { ++m_rightPosY; } else if (m_rightPosY > padY) { --m_rightPosY; } } /// bounce ball void Pong::bounceBall() { bounceBallSide(); bounceBallLeft(); bounceBallRight(); } /// bounce ball at sides void Pong::bounceBallSide() { if (m_ballPosY <= 0 && m_ballDirY < 0) { m_ballDirY = 1; } if (m_ballPosY >= m_height - 1 && m_ballDirY > 0) { m_ballDirY = -1; } if (m_ballPosX <= 0 && m_ballDirX < 0) { m_ballDirX = 1; } if (m_ballPosX >= m_width - 1 && m_ballDirX > 0) { m_ballDirX = -1; } } /// bounce ball at left pad void Pong::bounceBallLeft() { if (m_ballPosX != 1 || m_ballDirX >= 0) { return; } // top corner if (m_ballPosY == m_leftPosY - 1 && m_ballDirY > 0) { m_ballDirX = 1; m_ballDirY = -1; } // bottom corner else if (m_ballPosY == m_leftPosY + m_padSize && m_ballDirY < 0) { m_ballDirX = 1; m_ballDirY = 1; } // pad edge else if (m_ballPosY >= m_leftPosY && m_ballPosY < m_leftPosY + m_padSize) { m_ballDirX = 1; } } /// bounce ball at right pad void Pong::bounceBallRight() { if (m_ballPosX != m_width - 2 || m_ballDirX <= 0) { return; } // top corner if (m_ballPosY == m_rightPosY - 1 && m_ballDirY > 0) { m_ballDirX = -1; m_ballDirY = -1; } // bottom corner else if (m_ballPosY == m_rightPosY + m_padSize && m_ballDirY < 0) { m_ballDirX = -1; m_ballDirY = 1; } // pad edge else if (m_ballPosY >= m_rightPosY && m_ballPosY < m_rightPosY + m_padSize) { m_ballDirX = -1; } } /// request next time call - or cancel request if not needed void Pong::planTimeCall() { if (m_ballPosX < 0 || m_ballPosY < 0) { m_mgrs.m_callMgr.cancelTimeCall(this); return; } Time stepTime; stepTime.fromMs(100); m_mgrs.m_callMgr.requestTimeCall(this, Time::now() + stepTime); } /// move ball out of the field and halt it void Pong::hideBall() { m_ballPosX = -1; m_ballPosY = -1; m_ballDirX = 0; m_ballDirY = 0; // update time call request planTimeCall(); } /// start ball void Pong::startBall() { // ball starts horizontally at middle of field, vertically random m_ballPosX = (m_width - (rand() & 1)) / 2; m_ballPosY = rand() % m_height; // random diagonal direction m_ballDirX = (rand() & 1) * 2 - 1; m_ballDirY = (rand() & 1) * 2 - 1; // request first time call if needed planTimeCall(); } } // namespace Blinker