<commit_before>#include "aicontroller.h"

#include "hardware.h"
#include "gamefont.h"
#include "woopsistring.h"

// TODO: Doesn't check if it can rotate/move to the desired co-ordinate from
// the current position.  This should probably be re-examined every time the AI
// has the chance to move.

AIController::AIController(s32 hesitation) {
	_gridRunner = NULL;
	_lastLiveBlockY = Grid::GRID_HEIGHT;
	_targetX = 0;
	_targetRotations = 0;
	_hesitation = hesitation;
}

void AIController::setGridRunner(const GridRunner* gridRunner) {
	_gridRunner = gridRunner;
}

void AIController::analyseGrid() {

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	// If last observed y is greater than current live block y, we'll need
	// to choose a new move
	if (_lastLiveBlockY <= liveBlock1.y) {
		_lastLiveBlockY = liveBlock1.y < liveBlock2.y ? liveBlock1.y : liveBlock2.y;
		return;
	}

	_lastLiveBlockY = liveBlock1.y < liveBlock2.y ? liveBlock1.y : liveBlock2.y;

	// Get the y co-ords of the topmost blank block in each column
	s32* columnYCoords = new s32[Grid::GRID_WIDTH];

	for (s32 i = 0; i < Grid::GRID_WIDTH; ++i) {
		columnYCoords[i] = (Grid::GRID_HEIGHT - grid->getColumnHeight(i)) - 1;
	}

	// Work out which columns have heights equal to or greater than the current
	// live block Y co-ordinates and constrain the search to within the
	// boundaries that they create
	s32 leftBoundary = 0;
	s32 rightBoundary = Grid::GRID_WIDTH - 1;
	s32 lowestYCoord = liveBlock1.y > liveBlock2.y ? liveBlock1.y : liveBlock2.y;
	s32 leftBlockXCoord = liveBlock1.x < liveBlock2.x ? liveBlock1.x : liveBlock2.x;
	s32 rightBlockXCoord = liveBlock1.x > liveBlock2.x ? liveBlock1.x : liveBlock2.x;

	for (s32 i = leftBlockXCoord; i >= 0; --i) {
		if (columnYCoords[i] <= lowestYCoord) {
			leftBoundary = i;
			break;
		}
	}

	for (s32 i = rightBlockXCoord; i < Grid::GRID_WIDTH; ++i) {
		if (columnYCoords[i] <= lowestYCoord) {
			rightBoundary = i;
			break;
		}
	}

	BlockBase* block1 = grid->getBlockAt(liveBlock1.x, liveBlock1.y);
	BlockBase* block2 = grid->getBlockAt(liveBlock2.x, liveBlock2.y);

	s32 bestScore = 0;

	Point point1;
	Point point2;

	for (s32 x = leftBoundary + 1; x < rightBoundary; ++x) {
		for (s32 rotation = 0; rotation < 4; ++rotation) {

			// Work out where the shapes will be if they move, rotation occurs
			// and they land
			switch (rotation) {
				case 0:
					point1.x = x;
					point1.y = columnYCoords[x];

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1];
					break;

				case 1:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1] - 1;

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1];
					break;

				case 2:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1];

					point2.x = x;
					point2.y = columnYCoords[x];
					break;

				case 3:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1];

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1] - 1;
					break;
			}

			// Check if the new co-ords are valid
			if (point1.x < 0 || point1.x >= Grid::GRID_WIDTH) break;
			if (point1.y < 0 || point1.y >= Grid::GRID_HEIGHT) break;
			if (point2.x < 0 || point2.x >= Grid::GRID_WIDTH) break;
			if (point2.y < 0 || point2.y >= Grid::GRID_HEIGHT) break;

			s32 score = scoreShapePosition(block1, block2, point1, point2);

			// Bonus for not increasing the height of the target column
			s32 heightBonus = 1 + ((point1.y + point2.y) / 2);

			score *= heightBonus;

			// Check if the score for this position and rotation beats the
			// current best
			if (score > bestScore) {
				bestScore = score;
				_targetX = x;
				_targetRotations = rotation;
			}
		}
	}

	// We can rotate to the correct orientation faster by rotating anticlockwise
	// if necessary
	if (_targetRotations == 3) _targetRotations = -1;

	delete columnYCoords;
}

s32 AIController::scoreShapePosition(BlockBase* block1, BlockBase* block2, const Point& point1, const Point& point2) {

	const Grid* grid = _gridRunner->getGrid();

	s32 gridSize = Grid::GRID_WIDTH * Grid::GRID_HEIGHT;

	bool* checkedData = new bool[gridSize];

	for (s32 i = 0; i < gridSize; ++i) {
		checkedData[i] = false;
	}

	// Unfortunately, we can't get the score for each possible block position,
	// then add together pairs and see what the total score would be for each
	// possible rotation (this would have the number of times we walk the grid
	// graph).  If blocks are the same colour, and we do not examine the same
	// checkedData array whilst checking for chain lengths, we may end up
	// walking over the same blocks twice.  They will therefore be included in
	// the score multiple times (once for each block and once when the scores
	// are added together).  This would lead to positions where both blocks
	// touched same colour blocks being massively overweighted and possibly
	// supplant better positions.
	s32 score1 = grid->getPotentialChainLength(point1.x, point1.y, block1, checkedData);
	s32 score2 = grid->getPotentialChainLength(point2.x, point2.y, block2, checkedData);

	delete[] checkedData;

	s32 baseScore = score1 + score2;
	s32 extraScore = 0;

	if ((point1.x == point2.x || point1.y == point2.y) && (block1->getColour() == block2->getColour())) {
		extraScore = score1 + score2 - Grid::CHAIN_LENGTH;
	} else {
		extraScore = score1 > Grid::CHAIN_LENGTH ? 1 + score1 - Grid::CHAIN_LENGTH : 1;
		extraScore += score2 > Grid::CHAIN_LENGTH ? 1 + score2 - Grid::CHAIN_LENGTH : 1;
	}

	return baseScore * extraScore;
}

bool AIController::canMove() {

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	// Get the y co-ords of the topmost blank block in each column
	s32* columnYCoords = new s32[Grid::GRID_WIDTH];

	for (s32 i = 0; i < Grid::GRID_WIDTH; ++i) {
		columnYCoords[i] = (Grid::GRID_HEIGHT - grid->getColumnHeight(i)) - 1;
	}

	bool movePossible = true;

	if (_targetX < liveBlock1.x) {

		// Need to move left
		for (s32 x = liveBlock1.x; x >= _targetX; --x) {

			// If either block is lower than the column we're looking at, it is
			// impossible for the block to be moved to the target column
			if (columnYCoords[x] < liveBlock1.y || columnYCoords[x] < liveBlock2.y) {
				movePossible = false;
			}
		}
	} else if (_targetX > liveBlock1.x) {

		// Need to move right
		for (s32 x = liveBlock1.x; x <= _targetX; ++x) {
			if (columnYCoords[x] < liveBlock1.y || columnYCoords[x] < liveBlock2.y) {
				movePossible = false;
			}
		}
	}

	return movePossible;
}

bool AIController::left() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x > _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::right() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x < _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::down() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x == _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::rotateClockwise() {
	analyseGrid();

	if (_targetRotations > 0) {
		--_targetRotations;

		return _hesitation == 0 ? true : rand() % _hesitation == 0;
	}

	return false;
}

bool AIController::rotateAntiClockwise() {
	analyseGrid();

	if (_targetRotations < 0) {
		++_targetRotations;

		return _hesitation == 0 ? true : rand() % _hesitation == 0;
	}

	return false;
}
<commit_msg>Comment fix.<commit_after>#include "aicontroller.h"

#include "hardware.h"
#include "gamefont.h"
#include "woopsistring.h"

// TODO: Doesn't check if it can rotate/move to the desired co-ordinate from
// the current position.  This should probably be re-examined every time the AI
// has the chance to move.

AIController::AIController(s32 hesitation) {
	_gridRunner = NULL;
	_lastLiveBlockY = Grid::GRID_HEIGHT;
	_targetX = 0;
	_targetRotations = 0;
	_hesitation = hesitation;
}

void AIController::setGridRunner(const GridRunner* gridRunner) {
	_gridRunner = gridRunner;
}

void AIController::analyseGrid() {

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	// If last observed y is greater than current live block y, we'll need
	// to choose a new move
	if (_lastLiveBlockY <= liveBlock1.y) {
		_lastLiveBlockY = liveBlock1.y < liveBlock2.y ? liveBlock1.y : liveBlock2.y;
		return;
	}

	_lastLiveBlockY = liveBlock1.y < liveBlock2.y ? liveBlock1.y : liveBlock2.y;

	// Get the y co-ords of the topmost blank block in each column
	s32* columnYCoords = new s32[Grid::GRID_WIDTH];

	for (s32 i = 0; i < Grid::GRID_WIDTH; ++i) {
		columnYCoords[i] = (Grid::GRID_HEIGHT - grid->getColumnHeight(i)) - 1;
	}

	// Work out which columns have heights equal to or greater than the current
	// live block Y co-ordinates and constrain the search to within the
	// boundaries that they create
	s32 leftBoundary = 0;
	s32 rightBoundary = Grid::GRID_WIDTH - 1;
	s32 lowestYCoord = liveBlock1.y > liveBlock2.y ? liveBlock1.y : liveBlock2.y;
	s32 leftBlockXCoord = liveBlock1.x < liveBlock2.x ? liveBlock1.x : liveBlock2.x;
	s32 rightBlockXCoord = liveBlock1.x > liveBlock2.x ? liveBlock1.x : liveBlock2.x;

	for (s32 i = leftBlockXCoord; i >= 0; --i) {
		if (columnYCoords[i] <= lowestYCoord) {
			leftBoundary = i;
			break;
		}
	}

	for (s32 i = rightBlockXCoord; i < Grid::GRID_WIDTH; ++i) {
		if (columnYCoords[i] <= lowestYCoord) {
			rightBoundary = i;
			break;
		}
	}

	BlockBase* block1 = grid->getBlockAt(liveBlock1.x, liveBlock1.y);
	BlockBase* block2 = grid->getBlockAt(liveBlock2.x, liveBlock2.y);

	s32 bestScore = 0;

	Point point1;
	Point point2;

	for (s32 x = leftBoundary + 1; x < rightBoundary; ++x) {
		for (s32 rotation = 0; rotation < 4; ++rotation) {

			// Work out where the shapes will be if they move, rotation occurs
			// and they land
			switch (rotation) {
				case 0:
					point1.x = x;
					point1.y = columnYCoords[x];

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1];
					break;

				case 1:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1] - 1;

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1];
					break;

				case 2:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1];

					point2.x = x;
					point2.y = columnYCoords[x];
					break;

				case 3:
					point1.x = x + 1;
					point1.y = columnYCoords[x + 1];

					point2.x = x + 1;
					point2.y = columnYCoords[x + 1] - 1;
					break;
			}

			// Check if the new co-ords are valid
			if (point1.x < 0 || point1.x >= Grid::GRID_WIDTH) break;
			if (point1.y < 0 || point1.y >= Grid::GRID_HEIGHT) break;
			if (point2.x < 0 || point2.x >= Grid::GRID_WIDTH) break;
			if (point2.y < 0 || point2.y >= Grid::GRID_HEIGHT) break;

			s32 score = scoreShapePosition(block1, block2, point1, point2);

			// Bonus for not increasing the height of the target column
			s32 heightBonus = 1 + ((point1.y + point2.y) / 2);

			score *= heightBonus;

			// Check if the score for this position and rotation beats the
			// current best
			if (score > bestScore) {
				bestScore = score;
				_targetX = x;
				_targetRotations = rotation;
			}
		}
	}

	// We can rotate to the correct orientation faster by rotating anticlockwise
	// if necessary
	if (_targetRotations == 3) _targetRotations = -1;

	delete columnYCoords;
}

s32 AIController::scoreShapePosition(BlockBase* block1, BlockBase* block2, const Point& point1, const Point& point2) {

	const Grid* grid = _gridRunner->getGrid();

	s32 gridSize = Grid::GRID_WIDTH * Grid::GRID_HEIGHT;

	bool* checkedData = new bool[gridSize];

	for (s32 i = 0; i < gridSize; ++i) {
		checkedData[i] = false;
	}

	// Unfortunately, we can't get the score for each possible single block
	// position, then add together pairs and see what the total score would be
	// for each possible rotation (this would have the number of times we walk
	// the grid graph).  If blocks are the same colour, and we do not examine
	// the same checkedData array whilst checking for chain lengths, we may end
	// up walking over the same blocks twice.  They will therefore be included
	// in the score multiple times (once for each block and once when the scores
	// are added together).  This would lead to positions where both blocks
	// touched same colour blocks being massively overweighted and possibly
	// supplant better positions.
	s32 score1 = grid->getPotentialChainLength(point1.x, point1.y, block1, checkedData);
	s32 score2 = grid->getPotentialChainLength(point2.x, point2.y, block2, checkedData);

	delete[] checkedData;

	s32 baseScore = score1 + score2;
	s32 extraScore = 0;

	if ((point1.x == point2.x || point1.y == point2.y) && (block1->getColour() == block2->getColour())) {
		extraScore = score1 + score2 - Grid::CHAIN_LENGTH;
	} else {
		extraScore = score1 > Grid::CHAIN_LENGTH ? 1 + score1 - Grid::CHAIN_LENGTH : 1;
		extraScore += score2 > Grid::CHAIN_LENGTH ? 1 + score2 - Grid::CHAIN_LENGTH : 1;
	}

	return baseScore * extraScore;
}

bool AIController::canMove() {

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	// Get the y co-ords of the topmost blank block in each column
	s32* columnYCoords = new s32[Grid::GRID_WIDTH];

	for (s32 i = 0; i < Grid::GRID_WIDTH; ++i) {
		columnYCoords[i] = (Grid::GRID_HEIGHT - grid->getColumnHeight(i)) - 1;
	}

	bool movePossible = true;

	if (_targetX < liveBlock1.x) {

		// Need to move left
		for (s32 x = liveBlock1.x; x >= _targetX; --x) {

			// If either block is lower than the column we're looking at, it is
			// impossible for the block to be moved to the target column
			if (columnYCoords[x] < liveBlock1.y || columnYCoords[x] < liveBlock2.y) {
				movePossible = false;
			}
		}
	} else if (_targetX > liveBlock1.x) {

		// Need to move right
		for (s32 x = liveBlock1.x; x <= _targetX; ++x) {
			if (columnYCoords[x] < liveBlock1.y || columnYCoords[x] < liveBlock2.y) {
				movePossible = false;
			}
		}
	}

	return movePossible;
}

bool AIController::left() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x > _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::right() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x < _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::down() {
	analyseGrid();

	if (_targetRotations != 0) return false;

	const Grid* grid = _gridRunner->getGrid();

	Point liveBlock1;
	Point liveBlock2;

	grid->getLiveBlockPoints(liveBlock1, liveBlock2);

	bool result = liveBlock1.x == _targetX;

	return _hesitation == 0 ? result : result && (rand() % _hesitation == 0);
}

bool AIController::rotateClockwise() {
	analyseGrid();

	if (_targetRotations > 0) {
		--_targetRotations;

		return _hesitation == 0 ? true : rand() % _hesitation == 0;
	}

	return false;
}

bool AIController::rotateAntiClockwise() {
	analyseGrid();

	if (_targetRotations < 0) {
		++_targetRotations;

		return _hesitation == 0 ? true : rand() % _hesitation == 0;
	}

	return false;
}
<|endoftext|>


<commit_before>#include <iostream>

#include "yaml-cpp/yaml.h"  // IWYU pragma: keep

class NullEventHandler : public YAML::EventHandler {
 public:
  typedef YAML::Mark Mark;
  typedef YAML::anchor_t anchor_t;

  NullEventHandler() {}

  virtual void OnDocumentStart(const Mark&) {}
  virtual void OnDocumentEnd() {}
  virtual void OnNull(const Mark&, anchor_t) {}
  virtual void OnAlias(const Mark&, anchor_t) {}
  virtual void OnScalar(const Mark&, const std::string&, anchor_t,
                        const std::string&) {}
  virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t) {}
  virtual void OnSequenceEnd() {}
  virtual void OnMapStart(const Mark&, const std::string&, anchor_t) {}
  virtual void OnMapEnd() {}
};

int main() {
  YAML::Parser parser(std::cin);
  NullEventHandler handler;
  parser.HandleNextDocument(handler);
  return 0;
}
<commit_msg>Add missing include to read.cpp<commit_after>#include <iostream>

#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/yaml.h"  // IWYU pragma: keep

class NullEventHandler : public YAML::EventHandler {
 public:
  typedef YAML::Mark Mark;
  typedef YAML::anchor_t anchor_t;

  NullEventHandler() {}

  virtual void OnDocumentStart(const Mark&) {}
  virtual void OnDocumentEnd() {}
  virtual void OnNull(const Mark&, anchor_t) {}
  virtual void OnAlias(const Mark&, anchor_t) {}
  virtual void OnScalar(const Mark&, const std::string&, anchor_t,
                        const std::string&) {}
  virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t) {}
  virtual void OnSequenceEnd() {}
  virtual void OnMapStart(const Mark&, const std::string&, anchor_t) {}
  virtual void OnMapEnd() {}
};

int main() {
  YAML::Parser parser(std::cin);
  NullEventHandler handler;
  parser.HandleNextDocument(handler);
  return 0;
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>

#include "yaml-cpp/emitterstyle.h"
#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/yaml.h"  // IWYU pragma: keep

class NullEventHandler : public YAML::EventHandler {
 public:
  typedef YAML::Mark Mark;
  typedef YAML::anchor_t anchor_t;

  NullEventHandler() {}

  virtual void OnDocumentStart(const Mark&) {}
  virtual void OnDocumentEnd() {}
  virtual void OnNull(const Mark&, anchor_t) {}
  virtual void OnAlias(const Mark&, anchor_t) {}
  virtual void OnScalar(const Mark&, const std::string&, anchor_t,
                        const std::string&) {}
  virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t,
                               YAML::EmitterStyle::value style) {}
  virtual void OnSequenceEnd() {}
  virtual void OnMapStart(const Mark&, const std::string&, anchor_t,
                          YAML::EmitterStyle::value style) {}
  virtual void OnMapEnd() {}
};

void run(YAML::Parser& parser) {
  NullEventHandler handler;
  parser.HandleNextDocument(handler);
}

int main(int argc, char** argv) {
  if (argc > 1) {
    std::ifstream in(argv[1]);
    YAML::Parser parser(in);
    run(parser);
  } else {
    YAML::Parser parser(std::cin);
    run(parser);
  }
  return 0;
}
<commit_msg>Add features to read binary:<commit_after>#include <fstream>
#include <iostream>

#include "yaml-cpp/emitterstyle.h"
#include "yaml-cpp/eventhandler.h"
#include "yaml-cpp/yaml.h"  // IWYU pragma: keep

class NullEventHandler : public YAML::EventHandler {
 public:
  typedef YAML::Mark Mark;
  typedef YAML::anchor_t anchor_t;

  NullEventHandler() {}

  virtual void OnDocumentStart(const Mark&) {}
  virtual void OnDocumentEnd() {}
  virtual void OnNull(const Mark&, anchor_t) {}
  virtual void OnAlias(const Mark&, anchor_t) {}
  virtual void OnScalar(const Mark&, const std::string&, anchor_t,
                        const std::string&) {}
  virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t,
                               YAML::EmitterStyle::value style) {}
  virtual void OnSequenceEnd() {}
  virtual void OnMapStart(const Mark&, const std::string&, anchor_t,
                          YAML::EmitterStyle::value style) {}
  virtual void OnMapEnd() {}
};

void run(std::istream& in) {
  YAML::Parser parser(in);
  NullEventHandler handler;
  parser.HandleNextDocument(handler);
}

void usage() { std::cerr << "Usage: read [-n N] [-c, --cache] [filename]\n"; }

std::string read_stream(std::istream& in) {
  return std::string((std::istreambuf_iterator<char>(in)),
                     std::istreambuf_iterator<char>());
}

int main(int argc, char** argv) {
  int N = 1;
  bool cache = false;
  std::string filename;
  for (int i = 1; i < argc; i++) {
    std::string arg = argv[i];
    if (arg == "-n") {
      i++;
      if (i >= argc) {
        usage();
        return -1;
      }
      N = std::atoi(argv[i]);
      if (N <= 0) {
        usage();
        return -1;
      }
    } else if (arg == "-c" || arg == "--cache") {
      cache = true;
    } else {
      filename = argv[i];
      if (i + 1 != argc) {
        usage();
        return -1;
      }
    }
  }

  if (N > 1 && !cache && filename == "") {
    usage();
    return -1;
  }

  if (cache) {
    std::string input;
    if (filename != "") {
      std::ifstream in(filename);
      input = read_stream(in);
    } else {
      input = read_stream(std::cin);
    }
    std::istringstream in(input);
    for (int i = 0; i < N; i++) {
      in.seekg(std::ios_base::beg);
      run(in);
    }
  } else {
    if (filename != "") {
      std::ifstream in(filename);
      for (int i = 0; i < N; i++) {
        in.seekg(std::ios_base::beg);
        run(in);
      }
    } else {
      for (int i = 0; i < N; i++) {
        run(std::cin);
      }
    }
  }
  return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <nix/util/util.hpp>
#include <nix/hdf5/SimpleTagHDF5.hpp>
#include <nix/hdf5/RepresentationHDF5.hpp>
#include <nix/hdf5/DataSet.hpp>
#include <nix/Exception.hpp>

using namespace std;

namespace nix {
namespace hdf5 {

SimpleTagHDF5::SimpleTagHDF5(const SimpleTagHDF5 &tag)
    : EntityWithSourcesHDF5(tag.file(), tag.block(), tag.group(), tag.id()),
      references_list(tag.group(), "references")
{
    representation_group = tag.representation_group;
}

SimpleTagHDF5::SimpleTagHDF5(const File &file, const Block &block, const Group &group,
                             const string &id)
    : EntityWithSourcesHDF5(file, block, group, id), references_list(group, "references")
{
    representation_group = group.openGroup("representations");
}

SimpleTagHDF5::SimpleTagHDF5(const File &file, const Block &block, const Group &group,
                             const string &id, time_t time)
    : EntityWithSourcesHDF5(file, block, group, id, time), references_list(group, "references")
{
    representation_group = group.openGroup("representations");
}

vector<string> SimpleTagHDF5::units() const {
    vector<string> units;
    group().getData("units", units);
    return units;
}

void SimpleTagHDF5::units(vector<string> &units) {
    group().setData("units", units);
}

void SimpleTagHDF5::units(const none_t t) {
    if(group().hasData("units")) {
        group().removeData("units");
    }
    forceUpdatedAt();
}

vector<double> SimpleTagHDF5::position() const {
    vector<double> position;
    group().getData("position", position);
    return position;
}

void SimpleTagHDF5::position(const vector<double> &position) {
    group().setData("position", position);
}

vector<double> SimpleTagHDF5::extent() const {
    vector<double> extent;
    group().getData("extent", extent);
    return extent;
}

void SimpleTagHDF5::extent(const vector<double> &extent) {
    group().setData("extent", extent);
}

void SimpleTagHDF5::extent(const none_t t) {
    if(group().hasData("extent")) {
        group().removeData("extent");
    }
    forceUpdatedAt();
}

// Methods concerning references.

bool SimpleTagHDF5::hasReference(const std::string &id) const {
    return references_list.has(id);
}

size_t SimpleTagHDF5::referenceCount() const {
    return references_list.count();
}

DataArray SimpleTagHDF5::getReference(const std::string &id) const {
    if (hasReference(id)) {
        return block().getDataArray(id);
    } else {
        throw runtime_error("No reference with id: " + id);
    }
}

DataArray SimpleTagHDF5::getReference(size_t index) const {
    std::vector<std::string> refs = references_list.get();
    std::string id;

    // get reference id
    if(index < refs.size()) {
        id = refs[index];
    } else {
        throw OutOfBounds("No data array at given index", index);
    }
    // get referenced array
    if(block().hasDataArray(id)) {
        return block().getDataArray(id);
    } else {
        throw runtime_error("No data array id: " + id);
    }
}

void SimpleTagHDF5::addReference(const std::string &id) {
    if (!block().hasDataArray(id)) {
        throw runtime_error("Unable to find data array with reference_id " +
                            id + " on block " + block().id());
    }

    references_list.add(id);
}

bool SimpleTagHDF5::removeReference(const std::string &id) {
    return references_list.remove(id);
}

void SimpleTagHDF5::references(const std::vector<DataArray> &references) {
    vector<string> ids(references.size());

    for (size_t i = 0; i < references.size(); i++) {
        ids[i] = references[i].id();
    }

    references_list.set(ids);
}

// Methods concerning representations.

bool SimpleTagHDF5::hasRepresentation(const string &id) const {
    return representation_group.hasGroup(id);
}

size_t SimpleTagHDF5::representationCount() const {
    return representation_group.objectCount();
}

Representation SimpleTagHDF5::getRepresentation(const std::string &id) const {
    Group rep_g = representation_group.openGroup(id, false);
    auto tmp = make_shared<RepresentationHDF5>(file(), block(), rep_g, id);

    return Representation(tmp);
}

Representation SimpleTagHDF5::getRepresentation(size_t index) const{
    string rep_id = representation_group.objectName(index);
    Group rep_g = representation_group.openGroup(rep_id, false);
    auto tmp = make_shared<RepresentationHDF5>(file(), block(), rep_g, rep_id);

    return Representation(tmp);
}

Representation SimpleTagHDF5::createRepresentation(const std::string &data_array_id, LinkType link_type){
    string rep_id = util::createId("representation");
    while(representation_group.hasObject(rep_id))
        rep_id = util::createId("representation");
    Group rep_g = representation_group.openGroup(rep_id, true);
    auto tmp = make_shared<RepresentationHDF5>(file(), block(), rep_g, rep_id);
    tmp->linkType(link_type);
    tmp->data(data_array_id);

    return Representation(tmp);
}

bool SimpleTagHDF5::deleteRepresentation(const string &id){
    if (representation_group.hasGroup(id)) {
        representation_group.removeGroup(id);
        return true;
    } else {
        return false;
    }
}

// Other methods and functions

void SimpleTagHDF5::swap(SimpleTagHDF5 &other) {
    using std::swap;

    EntityWithSourcesHDF5::swap(other);
    swap(representation_group, other.representation_group);
    swap(references_list, other.references_list);
}

SimpleTagHDF5& SimpleTagHDF5::operator=(const SimpleTagHDF5 &other) {
    if (*this != other) {
        SimpleTagHDF5 tmp(other);
        swap(tmp);
    }
    return *this;
}

ostream& operator<<(ostream &out, const SimpleTagHDF5 &ent) {
    out << "SimpleTag: {name = " << ent.name();
    out << ", type = " << ent.type();
    out << ", id = " << ent.id() << "}";
    return out;
}

SimpleTagHDF5::~SimpleTagHDF5()
{
    // TODO Auto-generated destructor stub
}

} // namespace hdf5
} // namespace nix
<commit_msg>Modified (optional) getter of entities in "SimpleTag" to return empty entity if field missing.<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include <nix/util/util.hpp>
#include <nix/hdf5/SimpleTagHDF5.hpp>
#include <nix/hdf5/RepresentationHDF5.hpp>
#include <nix/hdf5/DataSet.hpp>
#include <nix/Exception.hpp>

using namespace std;

namespace nix {
namespace hdf5 {

SimpleTagHDF5::SimpleTagHDF5(const SimpleTagHDF5 &tag)
    : EntityWithSourcesHDF5(tag.file(), tag.block(), tag.group(), tag.id()),
      references_list(tag.group(), "references")
{
    representation_group = tag.representation_group;
}

SimpleTagHDF5::SimpleTagHDF5(const File &file, const Block &block, const Group &group,
                             const string &id)
    : EntityWithSourcesHDF5(file, block, group, id), references_list(group, "references")
{
    representation_group = group.openGroup("representations");
}

SimpleTagHDF5::SimpleTagHDF5(const File &file, const Block &block, const Group &group,
                             const string &id, time_t time)
    : EntityWithSourcesHDF5(file, block, group, id, time), references_list(group, "references")
{
    representation_group = group.openGroup("representations");
}

vector<string> SimpleTagHDF5::units() const {
    vector<string> units;
    group().getData("units", units);
    return units;
}

void SimpleTagHDF5::units(vector<string> &units) {
    group().setData("units", units);
}

void SimpleTagHDF5::units(const none_t t) {
    if(group().hasData("units")) {
        group().removeData("units");
    }
    forceUpdatedAt();
}

vector<double> SimpleTagHDF5::position() const {
    vector<double> position;
    group().getData("position", position);
    return position;
}

void SimpleTagHDF5::position(const vector<double> &position) {
    group().setData("position", position);
}

vector<double> SimpleTagHDF5::extent() const {
    vector<double> extent;
    group().getData("extent", extent);
    return extent;
}

void SimpleTagHDF5::extent(const vector<double> &extent) {
    group().setData("extent", extent);
}

void SimpleTagHDF5::extent(const none_t t) {
    if(group().hasData("extent")) {
        group().removeData("extent");
    }
    forceUpdatedAt();
}

// Methods concerning references.

bool SimpleTagHDF5::hasReference(const std::string &id) const {
    return references_list.has(id);
}

size_t SimpleTagHDF5::referenceCount() const {
    return references_list.count();
}

DataArray SimpleTagHDF5::getReference(const std::string &id) const {
    if (hasReference(id)) {
        // block will return empty object if entity not found
        return block().getDataArray(id);
    } else {
        throw runtime_error("No reference with id: " + id);
    }
}

DataArray SimpleTagHDF5::getReference(size_t index) const {
    std::vector<std::string> refs = references_list.get();
    std::string id;

    // get reference id
    if(index < refs.size()) {
        id = refs[index];
    } else {
        throw OutOfBounds("No data array at given index", index);
    }
    // get referenced array
    if(hasReference(id) && block().hasDataArray(id)) {
        return block().getDataArray(id);
    } else {
        throw runtime_error("No data array id: " + id);
    }
}

void SimpleTagHDF5::addReference(const std::string &id) {
    if (!block().hasDataArray(id)) {
        throw runtime_error("Unable to find data array with reference_id " +
                            id + " on block " + block().id());
    }

    references_list.add(id);
}

bool SimpleTagHDF5::removeReference(const std::string &id) {
    return references_list.remove(id);
}

void SimpleTagHDF5::references(const std::vector<DataArray> &references) {
    vector<string> ids(references.size());

    for (size_t i = 0; i < references.size(); i++) {
        ids[i] = references[i].id();
    }

    references_list.set(ids);
}

// Methods concerning representations.

bool SimpleTagHDF5::hasRepresentation(const string &id) const {
    return representation_group.hasGroup(id);
}

size_t SimpleTagHDF5::representationCount() const {
    return representation_group.objectCount();
}

Representation SimpleTagHDF5::getRepresentation(const std::string &id) const {
    if(hasRepresentation(id)) {
        auto tmp = make_shared<RepresentationHDF5>(file(), block(), representation_group.openGroup(id, false), id);
        return Representation(tmp);
    } else {
        return Representation();
    }
}

Representation SimpleTagHDF5::getRepresentation(size_t index) const{
    string id = representation_group.objectName(index);

    return getRepresentation(id);
}

Representation SimpleTagHDF5::createRepresentation(const std::string &data_array_id, LinkType link_type){
    string rep_id = util::createId("representation");
    while(representation_group.hasObject(rep_id))
        rep_id = util::createId("representation");
    Group rep_g = representation_group.openGroup(rep_id, true);
    auto tmp = make_shared<RepresentationHDF5>(file(), block(), rep_g, rep_id);
    tmp->linkType(link_type);
    tmp->data(data_array_id);

    return Representation(tmp);
}

bool SimpleTagHDF5::deleteRepresentation(const string &id){
    if (representation_group.hasGroup(id)) {
        representation_group.removeGroup(id);
        return true;
    } else {
        return false;
    }
}

// Other methods and functions

void SimpleTagHDF5::swap(SimpleTagHDF5 &other) {
    using std::swap;

    EntityWithSourcesHDF5::swap(other);
    swap(representation_group, other.representation_group);
    swap(references_list, other.references_list);
}

SimpleTagHDF5& SimpleTagHDF5::operator=(const SimpleTagHDF5 &other) {
    if (*this != other) {
        SimpleTagHDF5 tmp(other);
        swap(tmp);
    }
    return *this;
}

ostream& operator<<(ostream &out, const SimpleTagHDF5 &ent) {
    out << "SimpleTag: {name = " << ent.name();
    out << ", type = " << ent.type();
    out << ", id = " << ent.id() << "}";
    return out;
}

SimpleTagHDF5::~SimpleTagHDF5()
{
    // TODO Auto-generated destructor stub
}

} // namespace hdf5
} // namespace nix
<|endoftext|>


<commit_before>/*** tws_strat.cpp -- TWS strategy module
 *
 * Copyright (C) 2012 Ruediger Meier
 *
 * Author:  Ruediger Meier <sweet_f_a@gmx.de>
 *
 * This file is part of twstools.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the author nor the names of any contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ***/

#include "tws_strat.h"
#include "tws_query.h"
#include "tws_meta.h"
#include "tws_account.h"
#include "twsdo.h"
#include "debug.h"
#include <assert.h>

buy_sell_oid::buy_sell_oid() :
	sell_oid(0),
	buy_oid(0)
{
}

Strat::Strat( TwsDL& _twsdo) :
	twsdo(_twsdo)
{
}

Strat::~Strat()
{
}

/**
 * Return min tick price for a given contract.
 */
double Strat::min_tick(const IB::Contract& c)
{
	assert( c.conId != 0 );
	assert( twsdo.con_details.find( c.conId ) != twsdo.con_details.end() );
	double min_tick = twsdo.con_details[c.conId]->minTick ;
	assert(min_tick > 0.0);
	return min_tick;
}

/**
 * Return portfolio position of a given contract.
 */
int Strat::prtfl_pos(const IB::Contract& c)
{
	Prtfl::const_iterator it = twsdo.account->portfolio.find(c.conId);
	if( it !=  twsdo.account->portfolio.end() ) {
		return it->second.position;
	}
	return 0;
}

/**
 * Place or modify buy and sell orders for a single contract. Quote should
 * valid bid and ask.
 */
void Strat::adjust_order( const IB::Contract& c, const Quote& quote,
	buy_sell_oid& oids )
{
	PlaceOrder pO;
	pO.contract = c;
	pO.order.orderType = "LMT";
	pO.order.totalQuantity = pO.contract.secType == "CASH" ? 25000 : 1;
	const char *symbol = pO.contract.symbol.c_str();

	double quote_dist = 1 * min_tick(c);

	double lmt_buy = quote.val[IB::BID] - quote_dist;
	double lmt_sell = quote.val[IB::ASK] + quote_dist;

	if( twsdo.p_orders.find(oids.buy_oid) == twsdo.p_orders.end() ) {
		/* new buy order */
		DEBUG_PRINTF( "strat, new buy order %s", symbol );
		oids.buy_oid = twsdo.fetch_inc_order_id();
		pO.orderId = oids.buy_oid;
		pO.order.action = "BUY";
		pO.order.lmtPrice = lmt_buy;
		twsdo.workTodo->placeOrderTodo()->add(pO);
	} else {
		/* modify buy order */
		PacketPlaceOrder *ppo = twsdo.p_orders[oids.buy_oid];
		const PlaceOrder &po = ppo->getRequest();
		if( po.order.lmtPrice != lmt_buy ) {
			DEBUG_PRINTF( "strat, modify buy order %s", symbol );
			pO.orderId = oids.buy_oid;
			pO.order.action = "BUY";
			pO.order.lmtPrice = lmt_buy;
			twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	}
	if( twsdo.p_orders.find(oids.sell_oid) == twsdo.p_orders.end() ) {
		/* new sell order */
		DEBUG_PRINTF( "strat, new sell order %s", symbol );
		oids.sell_oid = twsdo.fetch_inc_order_id();
		pO.orderId = oids.sell_oid;
		pO.order.action = "SELL";
		pO.order.lmtPrice = lmt_sell;
		twsdo.workTodo->placeOrderTodo()->add(pO);
	} else {
		/* modify sell order */
		PacketPlaceOrder *ppo = twsdo.p_orders[oids.sell_oid];
		const PlaceOrder &po = ppo->getRequest();
		if( po.order.lmtPrice != lmt_sell ) {
			DEBUG_PRINTF( "strat, modify sell order %s", symbol );
			pO.orderId = oids.sell_oid;
			pO.order.action = "SELL";
			pO.order.lmtPrice = lmt_sell;
			twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	}
}

void Strat::adjustOrders()
{
	DEBUG_PRINTF( "strat, adjust orders" );

	const MktDataTodo &mtodo = twsdo.workTodo->getMktDataTodo();
	for( int i=0; i < mtodo.mktDataRequests.size(); i++ ) {
		const IB::Contract &contract = mtodo.mktDataRequests[i].ibContract;
		const Quote &quote = twsdo.quotes->at(i);

		/* here we also initialize zero order ids */
		buy_sell_oid &oids = map_data_order[i];

		if( quote.val[IB::BID] > 0.0 && quote.val[IB::ASK] > 0.0 ) {
			adjust_order( contract, quote, oids );
		} else {
			/* invalid quotes, TODO cleanup, cancel, whatever */
		}
	}
	DEBUG_PRINTF( "strat, place/modify %d orders",
		twsdo.workTodo->placeOrderTodo()->countLeft());
	assert( mtodo.mktDataRequests.size() == map_data_order.size() );
}<commit_msg>Strat, don't place new orders if we have that side already<commit_after>/*** tws_strat.cpp -- TWS strategy module
 *
 * Copyright (C) 2012 Ruediger Meier
 *
 * Author:  Ruediger Meier <sweet_f_a@gmx.de>
 *
 * This file is part of twstools.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the author nor the names of any contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ***/

#include "tws_strat.h"
#include "tws_query.h"
#include "tws_meta.h"
#include "tws_account.h"
#include "twsdo.h"
#include "debug.h"
#include <assert.h>

buy_sell_oid::buy_sell_oid() :
	sell_oid(0),
	buy_oid(0)
{
}

Strat::Strat( TwsDL& _twsdo) :
	twsdo(_twsdo)
{
}

Strat::~Strat()
{
}

/**
 * Return min tick price for a given contract.
 */
double Strat::min_tick(const IB::Contract& c)
{
	assert( c.conId != 0 );
	assert( twsdo.con_details.find( c.conId ) != twsdo.con_details.end() );
	double min_tick = twsdo.con_details[c.conId]->minTick ;
	assert(min_tick > 0.0);
	return min_tick;
}

/**
 * Return portfolio position of a given contract.
 */
int Strat::prtfl_pos(const IB::Contract& c)
{
	Prtfl::const_iterator it = twsdo.account->portfolio.find(c.conId);
	if( it !=  twsdo.account->portfolio.end() ) {
		return it->second.position;
	}
	return 0;
}

/**
 * Place or modify buy and sell orders for a single contract. Quote should
 * valid bid and ask.
 */
void Strat::adjust_order( const IB::Contract& c, const Quote& quote,
	buy_sell_oid& oids )
{
	PlaceOrder pO;
	pO.contract = c;
	pO.order.orderType = "LMT";
	pO.order.totalQuantity = pO.contract.secType == "CASH" ? 25000 : 1;
	const char *symbol = pO.contract.symbol.c_str();

	double quote_dist = 1 * min_tick(c);
	const int max_pos = 1;
	int cur_pos = prtfl_pos(c);

	double lmt_buy = quote.val[IB::BID] - quote_dist;
	double lmt_sell = quote.val[IB::ASK] + quote_dist;

	if( twsdo.p_orders.find(oids.buy_oid) == twsdo.p_orders.end() ) {
		if( cur_pos < max_pos ) {
		/* new buy order */
		DEBUG_PRINTF( "strat, new buy order %s", symbol );
		oids.buy_oid = twsdo.fetch_inc_order_id();
		pO.orderId = oids.buy_oid;
		pO.order.action = "BUY";
		pO.order.lmtPrice = lmt_buy;
		twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	} else {
		/* modify buy order */
		PacketPlaceOrder *ppo = twsdo.p_orders[oids.buy_oid];
		const PlaceOrder &po = ppo->getRequest();
		if( po.order.lmtPrice != lmt_buy ) {
			DEBUG_PRINTF( "strat, modify buy order %s", symbol );
			pO.orderId = oids.buy_oid;
			pO.order.action = "BUY";
			pO.order.lmtPrice = lmt_buy;
			twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	}
	if( twsdo.p_orders.find(oids.sell_oid) == twsdo.p_orders.end() ) {
		if( cur_pos > -max_pos ) {
		/* new sell order */
		DEBUG_PRINTF( "strat, new sell order %s", symbol );
		oids.sell_oid = twsdo.fetch_inc_order_id();
		pO.orderId = oids.sell_oid;
		pO.order.action = "SELL";
		pO.order.lmtPrice = lmt_sell;
		twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	} else {
		/* modify sell order */
		PacketPlaceOrder *ppo = twsdo.p_orders[oids.sell_oid];
		const PlaceOrder &po = ppo->getRequest();
		if( po.order.lmtPrice != lmt_sell ) {
			DEBUG_PRINTF( "strat, modify sell order %s", symbol );
			pO.orderId = oids.sell_oid;
			pO.order.action = "SELL";
			pO.order.lmtPrice = lmt_sell;
			twsdo.workTodo->placeOrderTodo()->add(pO);
		}
	}
}

void Strat::adjustOrders()
{
	DEBUG_PRINTF( "strat, adjust orders" );

	const MktDataTodo &mtodo = twsdo.workTodo->getMktDataTodo();
	for( int i=0; i < mtodo.mktDataRequests.size(); i++ ) {
		const IB::Contract &contract = mtodo.mktDataRequests[i].ibContract;
		const Quote &quote = twsdo.quotes->at(i);

		/* here we also initialize zero order ids */
		buy_sell_oid &oids = map_data_order[i];

		if( quote.val[IB::BID] > 0.0 && quote.val[IB::ASK] > 0.0 ) {
			adjust_order( contract, quote, oids );
		} else {
			/* invalid quotes, TODO cleanup, cancel, whatever */
		}
	}
	DEBUG_PRINTF( "strat, place/modify %d orders",
		twsdo.workTodo->placeOrderTodo()->countLeft());
	assert( mtodo.mktDataRequests.size() == map_data_order.size() );
}<|endoftext|>


<commit_before>/*!
@file
@author Nou (korewananda@gmail.com)

@brief Memory searcher and patching functionality to the RV engine.

This is the main patching suit for Intercept. It contains all the functionality
to search for and patch the RV engine. It also does the main memory analysis to
find and catalog the SQF function pointers and store them.

https://github.com/NouberNou/intercept
*/
#pragma once
#include "shared.hpp"
#include "singleton.hpp"
#include "logging.hpp"
#include "arguments.hpp"
#include "shared\types.hpp"
#include <unordered_set>

using namespace intercept::types;

namespace intercept {
    //! Nular function map.
    typedef std::unordered_map<std::string, std::vector<nular_entry>> nular_map;
    //! Unary function map.
    typedef std::unordered_map<std::string, std::vector<unary_entry>> unary_map;
    //! Binary functon map.
    typedef std::unordered_map<std::string, std::vector<binary_entry>> binary_map;

    struct sqf_register_functions {
        uintptr_t _gameState;
        uintptr_t _operator_construct;
        uintptr_t _operator_insert;
        uintptr_t _unary_construct;
        uintptr_t _unary_insert;
        std::array<uintptr_t, static_cast<size_t>(types::__internal::GameDataType::end)> _types {0};
    };

    /*!
    @brief The loader class, memory searcher and patching functionality to the RV engine.

    This is the main patching suit for Intercept. It contains all the functionality
    to search for and patch the RV engine. It also does the main memory analysis to
    find and catalog the SQF function pointers and store them.
    */
    class loader
        : public singleton<loader> {
    public:
        loader();
        ~loader();

        /*!
        @brief Walks the game state maps of SQF functions and finds their info.

        This walks the internal game state objects maps of SQF functions and then
        gets their comref information, arguement types, and the pointer to the
        actual function and stores them.
        */
        void do_function_walk(uintptr_t state_addr_);

        /*!
        @brief Returns a unary SQF function from the loaders library of found SQF functions.

        Returns a unary SQF function from the loaders library of found SQF functions.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the unary function.

        @return true if function is found, false if function is not found.

        @todo Throw exception if overloads are found so that unexpected results
        are not encountered.

        @code
        unary_function can_fire;
        bool found = get_function("canfire", can_fire);
        @endcode
        */
        bool get_function(std::string function_name_, unary_function &function_);

        /*!
        @brief Returns a unary SQF function from the loaders library of found
        SQF functions with argument signature.

        Returns a unary SQF function from the loaders library of found SQF functions
        but also takes a argument type in case there are overloaded versions of
        this SQF command available.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the unary function.
        @param [in] arg_signature_ The type of variable in SQF that the right
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT", etc.

        @return `true` if function is found, `false` if function is not found.

        @code
        unary_function random_function1;
        bool found1 = get_function("random", random_function1, "SCALAR");
        unary_function random_function2;
        bool found2 = get_function("random", random_function2, "ARRAY");
        @endcode
        */
        bool get_function(std::string function_name_, unary_function & function_, std::string arg_signature_);

        /*!
        @brief Returns a binary SQF function from the loaders library of found SQF functions.

        Returns a binary SQF function from the loaders library of found SQF functions.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.

        @return `true` if function is found, `false` if function is not found.

        @todo Throw exception if overloads are found so that unexpected results
        are not encountered.

        @code
        binary_function set_pos;
        bool found = get_function("setpos", set_pos);
        @endcode
        */
        bool get_function(std::string function_name_, binary_function &function_);

        /*!
        @brief Returns a binary SQF function from the loaders library of found
        SQF functions with argument signature.

        Returns a binary SQF function from the loaders library of found SQF functions
        but also takes a argument type in case there are overloaded versions of
        this SQF command available.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.
        @param [in] arg1_signature_ The type of variable in SQF that the left
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT",
        etc.
        @param [in] arg2_signature_ The type of variable in SQF that the right
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT",
        etc.

        @return `true` if function is found, `false` if function is not found.

        @code
        binary_function remove_menu_item1;
        bool found1 = get_function("removemenuitem", remove_menu_item1, "CONTROL", "SCALAR");
        binary_function remove_menu_item2;
        bool found2 = get_function("removemenuitem", remove_menu_item2, "CONTROL", "STRING");
        @endcode
        */
        bool get_function(std::string function_name_, binary_function &function_, std::string arg1_signature_, std::string arg2_signature_);

        /*!
        @brief Returns a nular SQF function from the loaders library of found SQF functions.

        Returns a nular SQF function from the loaders library of found SQF functions.
        There is no version of this function that takes a argument signature because
        there are no possible arguements for these functions and hence no possible
        overloading.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.

        @return `true` if function is found, `false` if function is not found.

        @code
        nular_function player;
        bool found = get_function("player", player);
        @endcode
        */
        bool get_function(std::string function_name_, nular_function &function_);

        /*!@{
        @brief Hook a function.

        Hooks a function so that when it is executed in SQF the hooked function
        will execute in its place instead.

        @warning Warning, this will only hook the first function (and in the future
        raise an exception if there is an overload of this function).

        @param [in] function_name_ The name of the function to hook.
        @param [in] hook_ A void pointer to the function to call instead.
        @param [out] trampoline_ A reference to the trampoline that stores the
        original function call.

        @return `true` if the hook succeded, `false` if the hook failed.
        */
        bool hook_function(std::string function_name_, void *hook_, unary_function &trampoline_);
        bool hook_function(std::string function_name_, void *hook_, binary_function &trampoline_);
        bool hook_function(std::string function_name_, void *hook_, nular_function &trampoline_);
        //!@}

        /*!@{
        @brief Unhook a unary function.

        Unhooks an already hooked functon. You must pass in the name, original
        hooked function (the `hook_` parameter that was passed in) and the trampoline
        that was assigned by the hook function.

        @param [in] function_name_ The name of the function to unhook.
        @param [in] hook_ A void pointer to the function that is being called in
        place of the original.
        @param [out] trampoline_ The trampoline that was returned via reference
        in the original hook.
        */
        bool unhook_function(std::string function_name_, void *hook_, unary_function &trampoline_);
        bool unhook_function(std::string function_name_, void *hook_, binary_function &trampoline_);
        bool unhook_function(std::string function_name_, void *hook_, nular_function &trampoline_);
        //!@}

        /*!@{
        @brief Return the associated function maps.
        */
        const unary_map & unary() const;
        const binary_map & binary() const;
        const nular_map & nular() const;
        //!@}

        /*!
        @brief Returns the pointer to the engines allocator functions.
        */
        const types::__internal::allocatorInfo* get_allocator() const;

        /*!
        @brief Returns function Pointers needed to register SQF Functions
        */
        const sqf_register_functions& get_register_sqf_info() const;

    protected:
        /*!
        @name Function Maps
        */
        //!@{
        unary_map _unary_operators;
        binary_map _binary_operators;
        nular_map _nular_operators;
        //!@}

        /*!
        @name Initial Hook Functionality
        */
        //!@{
        static int __cdecl _initial_patch(char *a_, int b_, int c_);
        static unary_function _initial_trampoline;
        //!@}

        /*!
        @brief Stores the hooked functions.
        */
        std::unordered_set<uint32_t> _hooked_functions;

        /*!
        @brief Stores the data about the engines allocators.
        */
        types::__internal::allocatorInfo _allocator;

        /*!
        @brief Stores the data about the Functions needed to register SQF Functions.
        */
        sqf_register_functions _sqf_register_funcs;

        bool _attached;
        bool _patched;
    };

    namespace __internal {	 //@Nou where should i store this stuff? It shall only be used internally.
        class gsFuncBase {
        public:
            const r_string _name;
        private:
            uint32_t placeholder1;//0x4
            uint32_t placeholder2;//0x8 actually a pointer to empty memory
            uint32_t placeholder3;//0xC
            uint32_t placeholder4;//0x10
            uint32_t placeholder5;//0x14
            uint32_t placeholder6;//0x18
            uint32_t placeholder7;//0x1C
            uint32_t placeholder8;//0x20
        };
        class gsFunction : public gsFuncBase {	//#TODO shouldn't everything in here be const?
            uint32_t placeholder9;//0x24
        public:
            const r_string _name2;//0x28 this is (tolower name)
            unary_operator * _operator;//0x2C
            uint32_t placeholder10;//0x30 RString to something
            const r_string _description;//0x34
            const r_string _example;
            const r_string _example2;
            const r_string placeholder11;
            const r_string placeholder12;
            const r_string _category; //0x48
                                        //const rv_string* placeholder13;
        };
        class gsOperator : public gsFuncBase {
            uint32_t placeholder9;//0x24  JNI function
        public:
            r_string _name2;//0x28 this is (tolower name)
            int32_t placeholder10; //0x2C Small int 0-5  priority
            binary_operator * _operator;//0x30
            r_string _leftType;//0x34 Description of left hand side parameter
            r_string _rightType;//0x38 Description of right hand side parameter
            r_string _description;//0x3C
            r_string _example;//0x40
            r_string placeholder11;//0x44
            r_string _version;//0x48 some version number
            r_string placeholder12;//0x4C
            r_string _category; //0x50
        };
        class gsNular : public gsFuncBase {
        public:
            const r_string _name2;//0x24 this is (tolower name)
            nular_operator * _operator;//0x28
            const r_string _description;//0x2C
            const r_string _example;
            const r_string _example2;
            const r_string _version;//0x38 some version number
            const r_string placeholder10;
            const r_string _category; //0x40
            uint32_t placeholder11;//0x44
        };
        struct gsTypeInfo { //Donated from ArmaDebugEngine
            const r_string _name;
            void* _createFunction{ nullptr };
        };
        struct game_functions : public auto_array<gsFunction>, public gsFuncBase {
        public:
            r_string _name;
            game_functions() {}
            const char *get_map_key() const { return _name; }
        };

        struct game_operators : public auto_array<gsOperator>, public gsFuncBase {
        public:
            r_string _name;
            int32_t placeholder10; //0x2C Small int 0-5  priority
            game_operators() {}
            const char *get_map_key() const { return _name; }
        };
        class game_state {  //ArmaDebugEngine is thankful for being allowed to contribute this.
        public:
            auto_array<const gsTypeInfo *> _scriptTypes;
            map_string_to_class<game_functions, auto_array<game_functions>> _scriptFunctions;
            map_string_to_class<game_operators, auto_array<game_operators>> _scriptOperators;
            map_string_to_class<gsNular, auto_array<gsNular>> _scriptNulars;
        };
    }

}<commit_msg>Fix Stuff<commit_after>/*!
@file
@author Nou (korewananda@gmail.com)

@brief Memory searcher and patching functionality to the RV engine.

This is the main patching suit for Intercept. It contains all the functionality
to search for and patch the RV engine. It also does the main memory analysis to
find and catalog the SQF function pointers and store them.

https://github.com/NouberNou/intercept
*/
#pragma once
#include "shared.hpp"
#include "singleton.hpp"
#include "logging.hpp"
#include "arguments.hpp"
#include "shared\types.hpp"
#include <unordered_set>

using namespace intercept::types;

namespace intercept {
    //! Nular function map.
    typedef std::unordered_map<std::string, std::vector<nular_entry>> nular_map;
    //! Unary function map.
    typedef std::unordered_map<std::string, std::vector<unary_entry>> unary_map;
    //! Binary functon map.
    typedef std::unordered_map<std::string, std::vector<binary_entry>> binary_map;

    struct sqf_register_functions {
        uintptr_t _gameState;
        uintptr_t _operator_construct;
        uintptr_t _operator_insert;
        uintptr_t _unary_construct;
        uintptr_t _unary_insert;
        std::array<uintptr_t, static_cast<size_t>(types::__internal::GameDataType::end)> _types {0};
    };

    /*!
    @brief The loader class, memory searcher and patching functionality to the RV engine.

    This is the main patching suit for Intercept. It contains all the functionality
    to search for and patch the RV engine. It also does the main memory analysis to
    find and catalog the SQF function pointers and store them.
    */
    class loader
        : public singleton<loader> {
    public:
        loader();
        ~loader();

        /*!
        @brief Walks the game state maps of SQF functions and finds their info.

        This walks the internal game state objects maps of SQF functions and then
        gets their comref information, arguement types, and the pointer to the
        actual function and stores them.
        */
        void do_function_walk(uintptr_t state_addr_);

        /*!
        @brief Returns a unary SQF function from the loaders library of found SQF functions.

        Returns a unary SQF function from the loaders library of found SQF functions.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the unary function.

        @return true if function is found, false if function is not found.

        @todo Throw exception if overloads are found so that unexpected results
        are not encountered.

        @code
        unary_function can_fire;
        bool found = get_function("canfire", can_fire);
        @endcode
        */
        bool get_function(std::string function_name_, unary_function &function_);

        /*!
        @brief Returns a unary SQF function from the loaders library of found
        SQF functions with argument signature.

        Returns a unary SQF function from the loaders library of found SQF functions
        but also takes a argument type in case there are overloaded versions of
        this SQF command available.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the unary function.
        @param [in] arg_signature_ The type of variable in SQF that the right
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT", etc.

        @return `true` if function is found, `false` if function is not found.

        @code
        unary_function random_function1;
        bool found1 = get_function("random", random_function1, "SCALAR");
        unary_function random_function2;
        bool found2 = get_function("random", random_function2, "ARRAY");
        @endcode
        */
        bool get_function(std::string function_name_, unary_function & function_, std::string arg_signature_);

        /*!
        @brief Returns a binary SQF function from the loaders library of found SQF functions.

        Returns a binary SQF function from the loaders library of found SQF functions.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.

        @return `true` if function is found, `false` if function is not found.

        @todo Throw exception if overloads are found so that unexpected results
        are not encountered.

        @code
        binary_function set_pos;
        bool found = get_function("setpos", set_pos);
        @endcode
        */
        bool get_function(std::string function_name_, binary_function &function_);

        /*!
        @brief Returns a binary SQF function from the loaders library of found
        SQF functions with argument signature.

        Returns a binary SQF function from the loaders library of found SQF functions
        but also takes a argument type in case there are overloaded versions of
        this SQF command available.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.
        @param [in] arg1_signature_ The type of variable in SQF that the left
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT",
        etc.
        @param [in] arg2_signature_ The type of variable in SQF that the right
        argument is. Must be in all caps, "ARRAY", "SCALAR", "BOOL", "OBJECT",
        etc.

        @return `true` if function is found, `false` if function is not found.

        @code
        binary_function remove_menu_item1;
        bool found1 = get_function("removemenuitem", remove_menu_item1, "CONTROL", "SCALAR");
        binary_function remove_menu_item2;
        bool found2 = get_function("removemenuitem", remove_menu_item2, "CONTROL", "STRING");
        @endcode
        */
        bool get_function(std::string function_name_, binary_function &function_, std::string arg1_signature_, std::string arg2_signature_);

        /*!
        @brief Returns a nular SQF function from the loaders library of found SQF functions.

        Returns a nular SQF function from the loaders library of found SQF functions.
        There is no version of this function that takes a argument signature because
        there are no possible arguements for these functions and hence no possible
        overloading.

        @param [in] function_name_ The name of the function, all in lowercase.
        @param [out] function_ A reference variable to the binary function.

        @return `true` if function is found, `false` if function is not found.

        @code
        nular_function player;
        bool found = get_function("player", player);
        @endcode
        */
        bool get_function(std::string function_name_, nular_function &function_);

        /*!@{
        @brief Hook a function.

        Hooks a function so that when it is executed in SQF the hooked function
        will execute in its place instead.

        @warning Warning, this will only hook the first function (and in the future
        raise an exception if there is an overload of this function).

        @param [in] function_name_ The name of the function to hook.
        @param [in] hook_ A void pointer to the function to call instead.
        @param [out] trampoline_ A reference to the trampoline that stores the
        original function call.

        @return `true` if the hook succeded, `false` if the hook failed.
        */
        bool hook_function(std::string function_name_, void *hook_, unary_function &trampoline_);
        bool hook_function(std::string function_name_, void *hook_, binary_function &trampoline_);
        bool hook_function(std::string function_name_, void *hook_, nular_function &trampoline_);
        //!@}

        /*!@{
        @brief Unhook a unary function.

        Unhooks an already hooked functon. You must pass in the name, original
        hooked function (the `hook_` parameter that was passed in) and the trampoline
        that was assigned by the hook function.

        @param [in] function_name_ The name of the function to unhook.
        @param [in] hook_ A void pointer to the function that is being called in
        place of the original.
        @param [out] trampoline_ The trampoline that was returned via reference
        in the original hook.
        */
        bool unhook_function(std::string function_name_, void *hook_, unary_function &trampoline_);
        bool unhook_function(std::string function_name_, void *hook_, binary_function &trampoline_);
        bool unhook_function(std::string function_name_, void *hook_, nular_function &trampoline_);
        //!@}

        /*!@{
        @brief Return the associated function maps.
        */
        const unary_map & unary() const;
        const binary_map & binary() const;
        const nular_map & nular() const;
        //!@}

        /*!
        @brief Returns the pointer to the engines allocator functions.
        */
        const types::__internal::allocatorInfo* get_allocator() const;

        /*!
        @brief Returns function Pointers needed to register SQF Functions
        */
        const sqf_register_functions& get_register_sqf_info() const;

    protected:
        /*!
        @name Function Maps
        */
        //!@{
        unary_map _unary_operators;
        binary_map _binary_operators;
        nular_map _nular_operators;
        //!@}

        /*!
        @name Initial Hook Functionality
        */
        //!@{
        static int __cdecl _initial_patch(char *a_, int b_, int c_);
        static unary_function _initial_trampoline;
        //!@}

        /*!
        @brief Stores the hooked functions.
        */
        std::unordered_set<uint32_t> _hooked_functions;

        /*!
        @brief Stores the data about the engines allocators.
        */
        types::__internal::allocatorInfo _allocator;

        /*!
        @brief Stores the data about the Functions needed to register SQF Functions.
        */
        sqf_register_functions _sqf_register_funcs;

        bool _attached;
        bool _patched;
    };

    namespace __internal {	 //@Nou where should i store this stuff? It shall only be used internally.
        class gsFuncBase {
        public:
            const r_string _name;
        private:
            uint32_t placeholder1;//0x4
            uint32_t placeholder2;//0x8 actually a pointer to empty memory
            uint32_t placeholder3;//0xC
            uint32_t placeholder4;//0x10
            uint32_t placeholder5;//0x14
            uint32_t placeholder6;//0x18
            uint32_t placeholder7;//0x1C
            uint32_t placeholder8;//0x20
        };
        class gsFunction : public gsFuncBase {	//#TODO shouldn't everything in here be const?
            uint32_t placeholder9;//0x24
        public:
            const r_string _name2;//0x28 this is (tolower name)
            unary_operator * _operator;//0x2C
            uint32_t placeholder10;//0x30 RString to something
            const r_string _description;//0x34
            const r_string _example;
            const r_string _example2;
            const r_string placeholder11;
            const r_string placeholder12;
            const r_string _category; //0x48
                                        //const rv_string* placeholder13;
        };
        class gsOperator : public gsFuncBase {
            uint32_t placeholder9;//0x24  JNI function
        public:
            r_string _name2;//0x28 this is (tolower name)
            int32_t placeholder10; //0x2C Small int 0-5  priority
            binary_operator * _operator;//0x30
            r_string _leftType;//0x34 Description of left hand side parameter
            r_string _rightType;//0x38 Description of right hand side parameter
            r_string _description;//0x3C
            r_string _example;//0x40
            r_string placeholder11;//0x44
            r_string _version;//0x48 some version number
            r_string placeholder12;//0x4C
            r_string _category; //0x50
        };
        class gsNular : public gsFuncBase {
        public:
            const r_string _name2;//0x24 this is (tolower name)
            nular_operator * _operator;//0x28
            const r_string _description;//0x2C
            const r_string _example;
            const r_string _example2;
            const r_string _version;//0x38 some version number
            const r_string placeholder10;
            const r_string _category; //0x40
            uint32_t placeholder11;//0x44
        };
        struct gsTypeInfo { //Donated from ArmaDebugEngine
            const r_string _name;
            void* _createFunction{ nullptr };
        };
        struct game_functions : public auto_array<gsFunction>, public gsFuncBase {
        public:
            r_string _name;
            game_functions() {}
            const char *get_map_key() const { return _name; }
        };

        struct game_operators : public auto_array<gsOperator>, public gsFuncBase {
        public:
            r_string _name;
            int32_t placeholder10; //0x2C Small int 0-5  priority
            game_operators() {}
            const char *get_map_key() const { return _name; }
        };
        class game_state {  //ArmaDebugEngine is thankful for being allowed to contribute this.
        public:
            auto_array<const gsTypeInfo *> _scriptTypes;
            map_string_to_class<game_functions, auto_array<game_functions>> _scriptFunctions;
            map_string_to_class<game_operators, auto_array<game_operators>> _scriptOperators;
            map_string_to_class<gsNular, auto_array<gsNular>> _scriptNulars;
        };
        template class rv_allocator<gsFunction>;
        template class rv_allocator<gsOperator>;
        template class rv_allocator<gsNular>;
    }

}<|endoftext|>


<commit_before>#ifndef IMPL_AX_SYSTEM_HPP
#define IMPL_AX_SYSTEM_HPP

#include <cstddef>
#include <stdexcept>
#include <memory>
#include <functional>
#include <array>
#include <unordered_set>
#include <unordered_map>
#include <queue>

#include "prelude.hpp"
#include "string.hpp"
#include "vector.hpp"
#include "option.hpp"
#include "address.hpp"
#include "addressable.hpp"
#include "castable.hpp"
#include "eventable.hpp"

namespace ax
{
    class entity;
    class system;
    using system_ptr = std::shared_ptr<ax::system>;
    class world;

    // A component in an entity-component-system.
    struct component
    {
        CONSTRAINT(component);
        bool active;
    };

    // A component that includes the address of the containing entity so that related components can be found.
    struct composite_component : public ax::component
    {
        CONSTRAINT(composite_component);
        ax::address address;
    };

    // The common data of an entity stored as a component.
    struct entity_component : public ax::component
    {
        std::unordered_map<ax::name, ax::component*> components;
    };

    // A component that store multiple of the same type of component.
    template<typename T, typename A, std::size_t N>
    struct multi_component : public ax::component
    {
        CONSTRAINT(multi_component);
        constexpr void static_check() { CONSTRAIN(T, component); }
        ax::vector<T, A, N> components;
    };

    // A transform component.
    // NOTE: of course, we'd instead use math library types in here.
    struct transform : public ax::component
    {
        float x_pos;
        float y_pos;
        float rotation;
    };

    // A system in an entity-component-system.
    class system : public ax::castable
    {
    public:

        CONSTRAINT(system);
        system(ax::world& world) : world(world) { }
        virtual ~system() = default;

        virtual ax::component& add_component(const ax::address& address) = 0;
        virtual bool remove_component(const ax::address& address) = 0;
        virtual ax::component* try_get_component(const ax::address& address) = 0;
        virtual void update(int mode = 0) = 0;

    protected:

        ENABLE_CAST(ax::system, ax::castable);
        ax::world& world;
    };

    template<typename T>
    class system_t : public ax::system
    {
    public:

        CONSTRAINT(system_t);
        using component_t = T;
        template<typename T> using reify = ax::system_t<T>;

        system_t(std::size_t capacity = 128)
        {
            CONSTRAIN(T, ax::component);
            components.reserve(capacity);
            component_map.reserve(capacity);
        }

        T& add_component(const ax::address& address, const T& component)
        {
            if (free_list.empty())
            {
                components.push_back(component);
                VAR& component_found = components.back();
                component_found.active = false;
                return component_found;
            }
            else
            {
                VAL index = free_list.front();
                free_list.pop();
                std::insert_or_assign(component_map, address, index);
                VAR& component_found = components.at(index);
                component_found = component;
                component_found.active = true; // ensure active after assignment
                return component_found;
            }
        }

        T& add_component(const ax::address& address) override
        {
            return ax::system_t<T>::add_component(address, T());
        }

        bool remove_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                free_list.push(index);
                VAR& component = components.at(index);
                component.active = false;
                component_map.erase(address);
                return true;
            }
            return false;
        }

        T* try_get_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                return &components.at(index);
            }
            return nullptr;
        }

        void update(int mode) override
        {
            for (VAR& component : components)
            {
                update_component(component, mode);
            }
        }

        virtual void update_component(T& component, int mode) = 0;

    protected:

        ENABLE_CAST(ax::system_t<T>, ax::system);

        ax::vector<T> components;
        std::unordered_map<ax::address, std::size_t> component_map;
        std::queue<std::size_t> free_list;
    };

    template<typename S, typename A, std::size_t N>
    class multi_system final : public ax::system_t<ax::multi_component<typename S::component_t, A, N>>
    {
    public:

        CONSTRAINT(multi_system);
        using component_t = typename S::component_t;
        using multi_component_t = typename ax::multi_component<component_t, A, N>;
        template<typename S, typename A, std::size_t N> using reify = ax::multi_system<S, A, N>;

        multi_system(S& system) : system(system) { }

        void update_component(multi_component_t& multicomponent, int mode) override
        {
            CONSTRAIN(S, ax::system_t<component_t>);
            for (VAR& component : multi_component.components)
            {
                system.update_component(component, mode);
            }
        }

    protected:

        using multi_system_s_a_n = ax::multi_system<S, A, N>;
        ENABLE_CAST(multi_system_s_a_n, ax::system_t<component_t>);

        S& system;
    };

    // The world that contains the entity-component-system, event system, and other mixins.
    // Uses function members because the type is not meant to be inherited.
    class world final : public ax::eventable<ax::world>
    {
    public:

        world(
            std::function<void(ax::world& world)> initialize_systems_impl,
            std::function<void(ax::world& world)> update_systems_impl,
            std::function<void(ax::world& world)> clean_up_systems_impl);

        template<typename T>
        T* try_add_component(const ax::name& system_name, const ax::address& address, const T& component = T())
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_component>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        VAR& result = system->add_component(address, component);
                        std::insert_or_assign(entity_opt->components, system_name, &result);
                        return &result;
                    }
                }
            }
            return nullptr;
        }

        template<typename T>
        T* try_get_component(const ax::name& system_name, const ax::address& address)
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_component>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        return system->try_get_component(address);
                    }
                }
            }
            return nullptr;
        }

        ax::transform* try_get_transform(const ax::address& address);
        ax::entity_component* try_get_entity(const ax::address& address);
        bool entity_exists(const ax::address& address);
        ax::component* try_add_component(const ax::name& system_name, const ax::address& address);
        bool try_remove_component(const ax::name& system_name, const ax::address& address);
        ax::entity create_entity(const ax::address& address);
        bool destroy_entity(const ax::address& address);
        ax::system_ptr try_add_system(const ax::name& name, ax::system_ptr system);
        bool remove_system(const ax::name& name);
        void initialize_systems();
        void update_systems();
        void clean_up_systems();

    private:

        ax::entity_component* try_add_entity(const ax::address& address);
        bool try_remove_entity(const ax::address& address);
        std::unordered_map<ax::name, std::shared_ptr<ax::system>> systems;
        std::function<void(ax::world& world)> initialize_systems_impl;
        std::function<void(ax::world& world)> update_systems_impl;
        std::function<void(ax::world& world)> clean_up_systems_impl;
    };

    // A handle to an entity in an entity-component-system.
    class entity final : public ax::addressable
    {
    public:

        entity(const ax::entity& other) = default;
        entity(ax::entity&& other) = default;
        ax::entity& operator=(const ax::entity& other) = default;
        ax::entity& operator=(ax::entity&& other) = default;
        entity(const ax::address& address, ax::world& world) : ax::addressable(address), world(world) { }

        template<typename T>
        const T* try_get_component(const ax::name& name) const { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        T* try_get_component(const ax::name& name) { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        const T& get_component(const ax::name& name) const { return *try_get_component<T>(name); }

        template<typename T>
        T& get_component(const ax::name& name)
        {
            VAR* component_opt = *try_get_component<T>(name);
            if (component_opt) return *component_opt;
            throw std::runtime_error("No component "_s + name.to_string() + " found at address " + get_address().to_string());
        }

        ax::transform& get_transform() const { return *world.try_get_transform(get_address()); }
        ax::transform& set_transform(const ax::transform& transform) { return get_transform() = transform; }
        float get_x_pos() const { return get_transform().x_pos; }
        float set_x_pos(float value) { return get_transform().x_pos = value; }
        float get_y_pos() const { return get_transform().y_pos; }
        float set_y_pos(float value) { return get_transform().y_pos = value; }
        float get_rotation() const { return get_transform().rotation; }
        float set_rotation(float value) { return get_transform().rotation = value; }
        bool exists() const { return world.entity_exists(get_address()); }

    private:

        ax::world& world;
    };
}

#endif
<commit_msg>Renamed multi_system.<commit_after>#ifndef IMPL_AX_SYSTEM_HPP
#define IMPL_AX_SYSTEM_HPP

#include <cstddef>
#include <stdexcept>
#include <memory>
#include <functional>
#include <array>
#include <unordered_set>
#include <unordered_map>
#include <queue>

#include "prelude.hpp"
#include "string.hpp"
#include "vector.hpp"
#include "option.hpp"
#include "address.hpp"
#include "addressable.hpp"
#include "castable.hpp"
#include "eventable.hpp"

namespace ax
{
    class entity;
    class system;
    using system_ptr = std::shared_ptr<ax::system>;
    class world;

    // A component in an entity-component-system.
    struct component
    {
        CONSTRAINT(component);
        bool active;
    };

    // A component that includes the address of the containing entity so that related components can be found.
    struct composite_component : public ax::component
    {
        CONSTRAINT(composite_component);
        ax::address address;
    };

    // The common data of an entity stored as a component.
    struct entity_component : public ax::component
    {
        std::unordered_map<ax::name, ax::component*> components;
    };

    // A component that store multiple of the same type of component.
    template<typename T, typename A, std::size_t N>
    struct multi_component : public ax::component
    {
        CONSTRAINT(multi_component);
        constexpr void static_check() { CONSTRAIN(T, component); }
        ax::vector<T, A, N> components;
    };

    // A transform component.
    // NOTE: of course, we'd instead use math library types in here.
    struct transform : public ax::component
    {
        float x_pos;
        float y_pos;
        float rotation;
    };

    // A system in an entity-component-system.
    class system : public ax::castable
    {
    public:

        CONSTRAINT(system);
        system(ax::world& world) : world(world) { }
        virtual ~system() = default;

        virtual ax::component& add_component(const ax::address& address) = 0;
        virtual bool remove_component(const ax::address& address) = 0;
        virtual ax::component* try_get_component(const ax::address& address) = 0;
        virtual void update(int mode = 0) = 0;

    protected:

        ENABLE_CAST(ax::system, ax::castable);
        ax::world& world;
    };

    template<typename T>
    class system_t : public ax::system
    {
    public:

        CONSTRAINT(system_t);
        using component_t = T;
        template<typename T> using reify = ax::system_t<T>;

        system_t(std::size_t capacity = 128)
        {
            CONSTRAIN(T, ax::component);
            components.reserve(capacity);
            component_map.reserve(capacity);
        }

        T& add_component(const ax::address& address, const T& component)
        {
            if (free_list.empty())
            {
                components.push_back(component);
                VAR& component_found = components.back();
                component_found.active = false;
                return component_found;
            }
            else
            {
                VAL index = free_list.front();
                free_list.pop();
                std::insert_or_assign(component_map, address, index);
                VAR& component_found = components.at(index);
                component_found = component;
                component_found.active = true; // ensure active after assignment
                return component_found;
            }
        }

        T& add_component(const ax::address& address) override
        {
            return ax::system_t<T>::add_component(address, T());
        }

        bool remove_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                free_list.push(index);
                VAR& component = components.at(index);
                component.active = false;
                component_map.erase(address);
                return true;
            }
            return false;
        }

        T* try_get_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                return &components.at(index);
            }
            return nullptr;
        }

        void update(int mode) override
        {
            for (VAR& component : components)
            {
                update_component(component, mode);
            }
        }

        virtual void update_component(T& component, int mode) = 0;

    protected:

        ENABLE_CAST(ax::system_t<T>, ax::system);

        ax::vector<T> components;
        std::unordered_map<ax::address, std::size_t> component_map;
        std::queue<std::size_t> free_list;
    };

    template<typename S, typename A, std::size_t N>
    class multi_system_t final : public ax::system_t<ax::multi_component<typename S::component_t, A, N>>
    {
    public:

        CONSTRAINT(multi_system_t);
        using component_t = typename S::component_t;
        using multi_component_t = typename ax::multi_component<component_t, A, N>;
        template<typename S, typename A, std::size_t N> using reify = ax::multi_system_t<S, A, N>;

        multi_system_t(S& system) : system(system) { }

        void update_component(multi_component_t& multicomponent, int mode) override
        {
            CONSTRAIN(S, ax::system_t<component_t>);
            for (VAR& component : multi_component.components)
            {
                system.update_component(component, mode);
            }
        }

    protected:

        using multi_system_s_a_n = ax::multi_system_t<S, A, N>;
        ENABLE_CAST(multi_system_s_a_n, ax::system_t<component_t>);

        S& system;
    };

    // The world that contains the entity-component-system, event system, and other mixins.
    // Uses function members because the type is not meant to be inherited.
    class world final : public ax::eventable<ax::world>
    {
    public:

        world(
            std::function<void(ax::world& world)> initialize_systems_impl,
            std::function<void(ax::world& world)> update_systems_impl,
            std::function<void(ax::world& world)> clean_up_systems_impl);

        template<typename T>
        T* try_add_component(const ax::name& system_name, const ax::address& address, const T& component = T())
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_component>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        VAR& result = system->add_component(address, component);
                        std::insert_or_assign(entity_opt->components, system_name, &result);
                        return &result;
                    }
                }
            }
            return nullptr;
        }

        template<typename T>
        T* try_get_component(const ax::name& system_name, const ax::address& address)
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_component>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        return system->try_get_component(address);
                    }
                }
            }
            return nullptr;
        }

        ax::transform* try_get_transform(const ax::address& address);
        ax::entity_component* try_get_entity(const ax::address& address);
        bool entity_exists(const ax::address& address);
        ax::component* try_add_component(const ax::name& system_name, const ax::address& address);
        bool try_remove_component(const ax::name& system_name, const ax::address& address);
        ax::entity create_entity(const ax::address& address);
        bool destroy_entity(const ax::address& address);
        ax::system_ptr try_add_system(const ax::name& name, ax::system_ptr system);
        bool remove_system(const ax::name& name);
        void initialize_systems();
        void update_systems();
        void clean_up_systems();

    private:

        ax::entity_component* try_add_entity(const ax::address& address);
        bool try_remove_entity(const ax::address& address);
        std::unordered_map<ax::name, std::shared_ptr<ax::system>> systems;
        std::function<void(ax::world& world)> initialize_systems_impl;
        std::function<void(ax::world& world)> update_systems_impl;
        std::function<void(ax::world& world)> clean_up_systems_impl;
    };

    // A handle to an entity in an entity-component-system.
    class entity final : public ax::addressable
    {
    public:

        entity(const ax::entity& other) = default;
        entity(ax::entity&& other) = default;
        ax::entity& operator=(const ax::entity& other) = default;
        ax::entity& operator=(ax::entity&& other) = default;
        entity(const ax::address& address, ax::world& world) : ax::addressable(address), world(world) { }

        template<typename T>
        const T* try_get_component(const ax::name& name) const { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        T* try_get_component(const ax::name& name) { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        const T& get_component(const ax::name& name) const { return *try_get_component<T>(name); }

        template<typename T>
        T& get_component(const ax::name& name)
        {
            VAR* component_opt = *try_get_component<T>(name);
            if (component_opt) return *component_opt;
            throw std::runtime_error("No component "_s + name.to_string() + " found at address " + get_address().to_string());
        }

        ax::transform& get_transform() const { return *world.try_get_transform(get_address()); }
        ax::transform& set_transform(const ax::transform& transform) { return get_transform() = transform; }
        float get_x_pos() const { return get_transform().x_pos; }
        float set_x_pos(float value) { return get_transform().x_pos = value; }
        float get_y_pos() const { return get_transform().y_pos; }
        float set_y_pos(float value) { return get_transform().y_pos = value; }
        float get_rotation() const { return get_transform().rotation; }
        float set_rotation(float value) { return get_transform().rotation = value; }
        bool exists() const { return world.entity_exists(get_address()); }

    private:

        ax::world& world;
    };
}

#endif
<|endoftext|>