diff --git a/community/c++-algo/.gitignore b/community/c++-algo/.gitignore new file mode 100644 index 00000000..aba41b15 --- /dev/null +++ b/community/c++-algo/.gitignore @@ -0,0 +1,918 @@ +##### Windows +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +##### Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +##### MacOS +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +##### Android +# Built application files +*.apk +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/caches +# Android Studio 3 in .gitignore file. +.idea/caches/build_file_checksums.ser +.idea/modules.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +##### GPG +secring.* + +##### Dropbox +# Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache + +##### SVN +.svn/ + +##### Mercurial +.hg/ +.hgignore +.hgsigs +.hgsub +.hgsubstate +.hgtags + +##### Bazaar +.bzr/ +.bzrignore + +##### CVS +/CVS/* +**/CVS/* +.cvsignore +*/.cvsignore + +##### TortoiseGit +# Project-level settings +/.tgitconfig + +##### Vim +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +##### Emacs +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + +##### SublimeText +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +##### Notepad++ +# Notepad++ backups # +*.bak + +##### TextMate +*.tmproj +*.tmproject +tmtags + +##### VisualStudioCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +##### NetBeans +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +##### JetBrains +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +##### Eclipse +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +##### Qt +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +##### MonoDevelop +# Mono Project +test-results/ + +*.pidb +*.resources + +# User Specific +*.userprefs +*.usertasks + +##### VisualStudio +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ +# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true +**/wwwroot/lib/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +##### Gradle +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +##### Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +##### CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +##### C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# errorFiles from Terminal engine +/*/errorFile*.txt + +# compiled starter kit binaries +algo-target/StarterAlgo diff --git a/community/c++-algo/CMakeLists.txt b/community/c++-algo/CMakeLists.txt new file mode 100644 index 00000000..2ea609f7 --- /dev/null +++ b/community/c++-algo/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required (VERSION 2.6) +project (StarterAlgo) + +set(out_dir ${CMAKE_SOURCE_DIR}/algo-target) +set(CMAKE_CXX_STANDARD 11) + +# First for the generic no-config case (e.g. with mingw) +set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${out_dir} ) +set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${out_dir}/lib ) +set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${out_dir}/lib ) +# Second, for multi-config builds (e.g. msvc) +foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${out_dir} ) + set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${out_dir}/lib ) + set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${out_dir}/lib ) +endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) + +set(HEADERS + src/algoStrategy.h + src/algoStrategy.cpp + src/source.cpp +) + +add_executable(StarterAlgo ${HEADERS}) + +target_include_directories (StarterAlgo PRIVATE ${CMAKE_SOURCE_DIR}/include/) + +add_subdirectory(${CMAKE_SOURCE_DIR}/include/json11/) +add_subdirectory(${CMAKE_SOURCE_DIR}/include/GameLib/) + +target_link_libraries(StarterAlgo json11 GameLib) diff --git a/community/c++-algo/README.md b/community/c++-algo/README.md new file mode 100644 index 00000000..f7add4c4 --- /dev/null +++ b/community/c++-algo/README.md @@ -0,0 +1,119 @@ +# Starter Algo C++ + +#### NOTE: This project is currently being developed, and is NOT supported by C1's servers yet. +This means you currently cannot upload this algo to their servers, it will not work. +However, if you have a linux OS it is possible compile and upload your compiled version and have it run. +This compilation will not work in the playground. + +### Directory Overview + +``` +community/c++-algo +│ +├───algo-target +│ run.ps1 +│ run.sh +│ +├───build +│ +├───documentation +│ +├───include +│ ├───GameLib +│ │ │ +│ │ └───src +│ │ algoCore.cpp +│ │ algoCore.h +│ │ customExceptions.h +│ │ enums.h +│ │ gameMap.cpp +│ │ gameMap.h +│ │ gameState.cpp +│ │ gameState.h +│ │ navigation.cpp +│ │ navigation.h +│ │ structs.h +│ │ unit.cpp +│ │ unit.h +│ │ util.cpp +│ │ util.h +│ │ +│ └───json11 +│ +└───src + algoStrategy.cpp + algoStrategy.h + source.cpp +``` + + +### Creating an algo + +For starters, simply modify the `src/algoStrategy.cpp` files. You will be mostly using functions in the `GameState` class. + +The `GameState` provides fast and convenient access to the game board, and the ability to perform actions, such as placing a +unit, which will mutate the `GameState` game state, as well as record that action. The GameState can then be used to submit your changes to the engine. + +**The standard output is used to communicate with the game engine, and must not be printed to.** +For this reason, debugging must be done with the standard error. The standard error messages are +available on the playground. As an abstraction over this logic, the `Util` method `debugWrite()` prints to the standard error stream. + +This project is intentionally modeled after the python-algo to make it familiar to those who are either new to programming in C++ or have been coding with terminal for a while. + +### Build script + +Since compiling will be different for every user, this is a CMake project to build for multiple enviornments. +This means for whatever enviornemnt you are using, you will need to install CMake (found [here](https://cmake.org/download/)). +In order to compile your algo, create a `build` directory under the `C1GamesStarterKit` directory and then run `cmake`. +Thus, the steps to setup a completely new enviornment would be (in terminal or cmd): +``` +C1GamesStarterKit\community\c++-algo> mkdir build +C1GamesStarterKit\community\c++-algo> cd build +C1GamesStarterKit\community\c++-algo\build> cmake .. +``` +(the commands are only after the `>`). + +This will create a project depending on what system you have avalible. +For example, if you have Visual Studio, it will generate a .sln project inside of `build` and you can then open +the project and build it. +If you are running linux, or something that supports makefiles, then while in the `build` directory you simply run: +``` +make +``` + +In all cases, the target for your executable is inside `c++-algo\algo-target`. + +Thus, to run a local game, you would give it the directory `community\c++-algo\algo-target`. + +Building this project has been tested using: +- Visual Studio 2017 +- Ubuntu 16.04.5 LTS +- Ubuntu 18.04.1 LTS +- MacOS Mojave + +### Documentation + +Documentation for this starter-kit can be generated using doxygen. The general steps to do so and explanation for their accepted style can be found on [their website](http://www.doxygen.nl/index.html). However, the steps that we took were: + +1. Install doxygen (found on their website). +2. Create folder to generate the documentation (`mkdir dox`) and go to it (`cd dox`). +3. Generate the config for doxygen (`doxygen -g dox-config`). +4. Modify the following options found in the dox-config file: + - EXTRACT_ALL = YES + - INPUT = ../ (or the path to the c++-algo code) + - RECURSIVE = YES + - JAVADOC_AUTOBRIEF = YES + - EXCLUDE_PATTERNS = \*/build/* + \*/json11/* + - USE_MDFILE_AS_MAINPAGE = ../README.md (or path to this README) + - STRIP_FROM_PATH (set it equal to the path up to and not including the C1GamesStarterKit) (eg C:/Users/.../) + +5. Run doxygen (`doxygen dox-config`). + +You can modify the output style further by specifying an html header/footer and css style (see [doxygen documentation](http://www.doxygen.nl/manual/customize.html). + +### Contributing + +This is a community made and supported algo. If you'd like to contribute a new feature +just reach out to me (@Isaac) on the [C1 forums](https://forum.c1games.com/) or submit a pull request with your changes. + +Feel free to submit a Pull Request for any bugs or new features, but please try and follow the style of the rest of the project. diff --git a/community/c++-algo/algo-target/run.ps1 b/community/c++-algo/algo-target/run.ps1 new file mode 100644 index 00000000..e97dbdd8 --- /dev/null +++ b/community/c++-algo/algo-target/run.ps1 @@ -0,0 +1,4 @@ +$scriptPath = Split-Path -parent $PSCommandPath; +$algoPath = "$scriptPath\StarterAlgo.exe" + +Invoke-Expression $algoPath diff --git a/community/c++-algo/algo-target/run.sh b/community/c++-algo/algo-target/run.sh new file mode 100755 index 00000000..c07d907e --- /dev/null +++ b/community/c++-algo/algo-target/run.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +"$DIR/StarterAlgo" diff --git a/community/c++-algo/include/GameLib/CMakeLists.txt b/community/c++-algo/include/GameLib/CMakeLists.txt new file mode 100644 index 00000000..7c20c8cc --- /dev/null +++ b/community/c++-algo/include/GameLib/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required (VERSION 2.6) +project (GameLib) + +set(HEADERS + src/customExceptions.h + src/structs.h + src/enums.h + src/util.h + src/util.cpp + src/algoCore.h + src/algoCore.cpp + src/gameState.h + src/gameState.cpp + src/gameMap.h + src/gameMap.cpp + src/unit.h + src/unit.cpp + src/navigation.h + src/navigation.cpp +) + +add_library(GameLib ${HEADERS}) + +target_include_directories (GameLib PRIVATE ${CMAKE_SOURCE_DIR}/include/) + +target_link_libraries(GameLib json11) diff --git a/community/c++-algo/include/GameLib/README.md b/community/c++-algo/include/GameLib/README.md new file mode 100644 index 00000000..c1fbba44 --- /dev/null +++ b/community/c++-algo/include/GameLib/README.md @@ -0,0 +1,8 @@ +### GameLib Library + +This is a small, simple library to make interfacing with the Terminal engine easier. +It is very heavily modeled after the `python-algo` gamelib project. + +It contains classes to make it easier to submit your turns and get the information from the terminal engine. + +It also contains a `GameState` class which contains functions to allow you to create theoretical game states and to test strategies before you submit them to the engine. This includes things like pathfinding and predicting future resources. diff --git a/community/c++-algo/include/GameLib/src/algoCore.cpp b/community/c++-algo/include/GameLib/src/algoCore.cpp new file mode 100644 index 00000000..78b321ec --- /dev/null +++ b/community/c++-algo/include/GameLib/src/algoCore.cpp @@ -0,0 +1,115 @@ +/* +Description: The default object to override for your strategy. +Last Modified: 08 Apr 2019 +Author: Isaac Draper +*/ + +#include "algoCore.h" + +namespace terminal { + + using json11::Json; + + /// Basic constructor for algoCore. + AlgoCore::AlgoCore() { + endOfGame = false; + } + + /// A function that runs at the start of every game. + /// Override this to perform initial setup at the start of the game, based on the configuration. + /// @param configuration A Json object containing information about the game. + void AlgoCore::onGameStart(Json configuration) { + config = configuration; + } + + /// Called when the engine expects input from the algo. + /// It is called every turn and is passed a Json object containing + /// the current game state, which can be used to initialize a new gameState object. + /// @param gameState A Json object containing the current game state. + void AlgoCore::onTurn(Json gameState) { + AlgoCore::submitDefaultTurn(); + } + + /// Submits a turn that does nothing. + void AlgoCore::submitDefaultTurn() const { + Util::sendCommand("[]"); + Util::sendCommand("[]"); + } + + /// Starts the main loop of the program. + /// This will continue until it gets the end of state signal from the engine. + /// If there is some error, it will never exit since it will continue + /// to wait until it recieves input from the engine. + /// If this happens it must be killed manually. + void AlgoCore::start() { + Util::debugWrite("Starting C++ Starter Algo"); + + while (true) { + try { + Json gameState = Util::getCommand(); + + if (gameState["turnInfo"] == nullptr) { + // Here we know it is the first turn, and the gameState object + // is information about the game, not an actual game state. + // Thus, we run the setup for the game. + + onGameStart(gameState); + } + else { + int stateType = gameState["turnInfo"].array_items().at(0).int_value(); + + // We now make decisions based on what turn state it is. + switch (stateType) { + case 0: { + // This is when the engine expects to recieve what we are + // building for this turn. + // So we run the onTurn function. + + onTurn(gameState); + break; + } + case 1: { + // This is a single frame from the engine. + // If you want to get information from the game every frame, + // you can create function to do that here. + + break; + } + case 2: { + // This indicates that the game has ended. + // Since break simply takes us out of the switch, + // we use a variable to quite the while loop. + + Util::debugWrite("GOT END OF STATE"); + endOfGame = true; + break; + } + default: { + // This means we got an unexpected string. + // This technically should never happen since it would + // be caught by the Json parser. + + Util::debugWrite("Unexpected state recieved: " + std::to_string(stateType)); + break; + } + } + + if (endOfGame) { + break; + } + } + } + catch (UtilException e) { + Util::debugWrite(e.what()); + continue; + } + } + } + + /// This returns a string representation of the AlgoCore object. + /// @return A string to represent this object. + std::string AlgoCore::toString() const { + return "AlgoCore"; + } + +} diff --git a/community/c++-algo/include/GameLib/src/algoCore.h b/community/c++-algo/include/GameLib/src/algoCore.h new file mode 100644 index 00000000..c97b7ea1 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/algoCore.h @@ -0,0 +1,54 @@ +/* +Description: This contains the core components necessary for an algo to run. +Last Modified: 08 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef ALGO_CORE_H +#define ALGO_CORE_H + +#include +#include + +#include "json11/json11.hpp" +#include "customExceptions.h" +#include "util.h" + +namespace terminal { + + using json11::Json; + + /// This is the super class to contain the basic essentials. + /// You should inherit from this class when developing your strategy. + class AlgoCore { + public: + // Functions + AlgoCore(); + virtual void start(); + virtual std::string toString() const; + + protected: + // Functions + virtual void onGameStart(Json configuration); + virtual void onTurn(Json gameState); + void submitDefaultTurn() const; + + // Members + Json config; ///< Holds information about the game. + + private: + // Members + bool endOfGame; ///< Keeps track of whether the game has ended. + + }; + + /// This sends a representation of the AlgoCore object to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, AlgoCore const& algoCore) { + os << algoCore.toString(); + return os; + } + +} + +#endif diff --git a/community/c++-algo/include/GameLib/src/customExceptions.h b/community/c++-algo/include/GameLib/src/customExceptions.h new file mode 100644 index 00000000..087862c8 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/customExceptions.h @@ -0,0 +1,75 @@ +/* +Description: This is a header to contain custom exceptions for the terminal game. +Last Modified: 06 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef CUSTOM_EXCEPTIONS_H +#define CUSTOM_EXCEPTIONS_H + +#include +#include + +namespace terminal { + + using std::string; + + /// A class to act as a custom exception. + /// It is primarily to be inherrited by any custom exception used by the project. + class CustomException : public std::exception { + public: + CustomException(const string errorMsg="Custom Exception") { + msg = errorMsg; + } + const char* what() { + return msg.c_str(); + } + protected: + string msg; ///< Stores the message for the error. + }; + + /// A Custom Exception to be thrown by any utility critical errors. + class UtilException : public CustomException { + public: + UtilException(const string errorMsg = "Util Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when there is something wrong with a Unit Type. + class UnitTypeException : public CustomException { + public: + UnitTypeException(const string errorMsg = "Unit Type Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when there is something wrong with spawning a unit. + class UnitSpawnException : public CustomException { + public: + UnitSpawnException(const string errorMsg = "Unit Spawn Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when there is something wrong with removing a unit. + class UnitRemoveException : public CustomException { + public: + UnitRemoveException(const string errorMsg = "Unit Remove Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when there is something wrong with a position. + class PosException : public CustomException { + public: + PosException(const string errorMsg = "Pos Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when an invalid player index is used. + class PlayerIndexException : public CustomException { + public: + PlayerIndexException(const string errorMsg = "Player Index Exception") : CustomException(errorMsg) {} + }; + + /// An exception to be thrown when something wrong happens with the GameMap. + class GameMapException : public CustomException { + public: + GameMapException(const string errorMsg = "Game Map Exception") : CustomException(errorMsg) {} + }; + +} + +#endif diff --git a/community/c++-algo/include/GameLib/src/enums.h b/community/c++-algo/include/GameLib/src/enums.h new file mode 100644 index 00000000..d989b8c6 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/enums.h @@ -0,0 +1,42 @@ +/* +Description: Contains useful enums describing game data. +Last Modified: 11 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef ENUMS_H +#define ENUMS_H + +namespace terminal { + + enum UNIT_TYPE { + FILTER, ///< Represents a filter unit type. + ENCRYPTOR, ///< Represents an encryptor unit type. + DESTRUCTOR, ///< Represents a destructor unit type. + PING, ///< Represents a ping unit type. + EMP, ///< Represents an emp unit type. + SCRAMBLER, ///< Represents a scrambler unit type. + REMOVE ///< Represents a remove command. + }; + + enum RESOURCE { + BITS, ///< Represents the bit resource. + CORES ///< Represents the core resource. + }; + + enum EDGE { + TOP_RIGHT, ///< Represents the top right edge of the map. + TOP_LEFT, ///< Represents the top left edge of the map. + BOTTOM_LEFT , ///< Represents the bottom left edge of the map. + BOTTOM_RIGHT ///< Represents the bottom right edge of the map. + }; + + enum VERBOSITY { + SUPPRESS, ///< Means no errors will be printed. + WARNING, ///< Will print warning level message. + INVARIANT, ///< Will throw invariant level and below exceptions. + CRASH, ///< Will throw all types of errors. + }; +} + +#endif diff --git a/community/c++-algo/include/GameLib/src/gameMap.cpp b/community/c++-algo/include/GameLib/src/gameMap.cpp new file mode 100644 index 00000000..62d04e1f --- /dev/null +++ b/community/c++-algo/include/GameLib/src/gameMap.cpp @@ -0,0 +1,299 @@ +/* +Description: Defines the implementations for the game map. +Last Modified: 10 Apr 2019 +Author: Isaac Draper, Ryan Draves +*/ + +#include "gameMap.h" + +namespace terminal { + + using json11::Json; + using std::vector; + + /// Constructor for GameMap which requires a configuration Json object. + /// @param configuration A Json object containing information about the game. + GameMap::GameMap(Json config) { + this->config = config; + this->verbosity = WARNING; + createEmptyGrid(); + } + + /// Fills map with an emtpy grid of dimensions ARENA_SIZE. + void GameMap::createEmptyGrid() { + map.resize(ARENA_SIZE); + for (unsigned int x = 0; x < ARENA_SIZE; ++x) + map.at(x).resize(ARENA_SIZE); + } + + /// Checks if a position is inside the diamond shaped game board. + /// @param x The x position. + /// @param y The y position. + bool GameMap::inArenaBounds(int x, int y) const { + int rowSize = y + 1; + int startX = HALF_ARENA - rowSize; + int endX = startX + (2 * rowSize) - 1; + const bool topHalfCheck = y < HALF_ARENA && x >= startX && x <= endX; + + rowSize = (ARENA_SIZE - 1 - y) + 1; + startX = HALF_ARENA - rowSize; + endX = startX + (2 * rowSize) - 1; + const bool bottomHalfCheck = y >= HALF_ARENA && x >= startX && x <= endX; + + return bottomHalfCheck || topHalfCheck; + } + + /// Checks if a position is inside the diamond shaped game board. + /// @param pos The position to check. + bool GameMap::inArenaBounds(Pos pos) const { + return inArenaBounds(pos.x, pos.y); + } + + /// Takes an edge and appends a list of location on that edge to a vector. + /// @param vec A vector passed by reference you would like to fill. + /// @param edge The edge to get units for. + void GameMap::getEdgeLocations(vector& vec, const EDGE edge) const { + unsigned int x, y; + switch (edge) { + case TOP_RIGHT: { + for (unsigned int i = 0; i < HALF_ARENA; ++i) { + x = HALF_ARENA + i; + y = ARENA_SIZE - 1 - i; + vec.push_back(Pos{ x, y }); + } + break; + } + case TOP_LEFT: { + for (unsigned int i = 0; i < HALF_ARENA; ++i) { + x = HALF_ARENA - 1 - i; + y = ARENA_SIZE - 1 - i; + vec.push_back(Pos{ x, y }); + } + break; + } + case BOTTOM_LEFT: { + for (unsigned int i = 0; i < HALF_ARENA; ++i) { + x = HALF_ARENA - 1 - i; + y = i; + vec.push_back(Pos{ x, y }); + } + break; + } + case BOTTOM_RIGHT: { + for (unsigned int i = 0; i < HALF_ARENA; ++i) { + x = HALF_ARENA + i; + y = i; + vec.push_back(Pos{ x, y }); + } + break; + } + default: { + Util::printError("Invalid edge requested", INVARIANT, verbosity); + } + } + } + + /// Fills out a vector of all the edges. + /// @param vec A vector of { topRight, topLeft, bottomLeft, bottomRight } edges. + void GameMap::getEdges(vector>& vec) const { + vector topRight, topLeft, bottomLeft, bottomRight; + + getEdgeLocations(topRight, TOP_RIGHT); + getEdgeLocations(topLeft, TOP_LEFT); + getEdgeLocations(bottomLeft, BOTTOM_LEFT); + getEdgeLocations(bottomRight, BOTTOM_RIGHT); + + vec.emplace_back(topRight); + vec.emplace_back(topLeft); + vec.emplace_back(bottomLeft); + vec.emplace_back(bottomRight); + } + + /// Add a single GameUnit to the map at the given location. + /// This does not send it to the engine, it simply lets you create any + /// map position you want to experiment with. + /// @param unitType The type of unit to add. Stationary units will replace. + /// @param pos The position to add the unit at. + /// @param playerIndex The player to add the unit for. + /// @param hp The health of the unit (default is max health). + void GameMap::addUnit(UNIT_TYPE unitType, Pos pos, int playerIndex, double hp) { + if (!inArenaBounds(pos)) + Util::printError("Out of bounds exception", CRASH, verbosity); + if (playerIndex < 0 || playerIndex > 1) + throw PlayerIndexException(); + + // Stability of 0 will default the unit to max_stability + GameUnit newUnit = GameUnit(unitType, config, hp, playerIndex, pos[0], pos[1]); + + if (!newUnit.stationary) { + map.at(pos.x).at(pos.y).push_back(newUnit); + } + else { + size_t size = map.at(pos.x).at(pos.y).size(); + if (size > 0 && newUnit.unitType != REMOVE) { + Util::printError("Error placing a stationary unit in an occupied location", INVARIANT, verbosity); + } + else if ((size != 1 || (size > 0 && !map.at(pos.x).at(pos.y).at(0).stationary)) + && newUnit.unitType == REMOVE) { + Util::printError("Error placing remove; 1 stationary unit not found", INVARIANT, verbosity); + } + + // Clearing the vector for firewalls is not necessary, as we verified above + map.at(pos.x).at(pos.y).push_back(newUnit); + } + } + + + /// Add a single GameUnit to the map at the given location. + /// This does not send it to the engine, it simply lets you create any + /// map position you want to experiment with. + /// @param unitType The type of unit to add. Stationary units will replace. + /// @param x The x position to add the unit at. + /// @param y The y position to add the unit at. + /// @param playerIndex The player to add the unit for. + /// @param hp The health of the unit (default is max health). + void GameMap::addUnit(UNIT_TYPE unitType, int x, int y, int playerIndex, double hp) { + addUnit(unitType, Pos(x, y), playerIndex, hp); + } + + /// Remove all GameUnits from the game map at the location. + /// This will throw an error if the location is empty. + /// @param pos Position to clear from the game map. + void GameMap::removeUnits(Pos pos) { + if (!inArenaBounds(pos)) Util::printError("Out of bounds exception", CRASH, verbosity); + if (map.at(pos.x).at(pos.y).size() == 0) Util::printError("Error trying to remove 0 units", WARNING, verbosity); + + map.at(pos.x).at(pos.y).clear(); + } + + /// Takes a position and radius and fills a vector with locations in its range. + /// @param locations A vector passed by reference to return the values. + /// @param pos The position to find locatins in range from. + /// @param radius The radius of the circle to get locations from. + void GameMap::getLocationsInRange(vector& locations, Pos pos, double radius) const { + if (radius < 0 || radius > ARENA_SIZE) + Util::printError("Error getting locations in range with that radius", INVARIANT, verbosity); + if (!inArenaBounds(pos)) + Util::printError("Out of bounds exception", CRASH, verbosity); + + for (int i = (int)(pos.x - radius); i < (int)(pos.x + radius + 1); i++) { + for (int j = (int)(pos.y - radius); j < (int)(pos.y + radius + 1); j++) { + if (i < 0 || j < 0) continue; + Pos new_pos = { (unsigned int)i, (unsigned int)j }; + if (inArenaBounds(new_pos) && + distanceBetweenLocations(pos, new_pos) < radius + 0.51) { + locations.push_back(new_pos); + } + } + } + } + + /// Checks whether a stationary unit is located at a position. + /// @param pos Position to check on the map. + /// @return Boolean answer to a stationary unit at the location. + bool GameMap::containsStationaryUnit(Pos pos) const { + if (!inArenaBounds(pos)) + Util::printError("Out of bounds exception", CRASH, verbosity); + + if (map.at(pos.x).at(pos.y).size() > 0) { + switch (map.at(pos.x).at(pos.y).at(0).unitType) { + case FILTER: + case ENCRYPTOR: + case DESTRUCTOR: + return true; + break; + default: + return false; + } + } + return false; + } + + /// Checks whether a stationary unit is located at a position. + /// @param x X coordinate to check on the map. + /// @param y Y coordinate to check on the map. + /// @return Boolean answer to a stationary unit at the location. + bool GameMap::containsStationaryUnit(int x, int y) const { + return containsStationaryUnit(Pos(x, y)); + } + + /// Gives the euclidean distance between two locations. + /// @param pos1 First position. + /// @param pos2 Second position. + /// @return Euclidean distance between two positions. + double GameMap::distanceBetweenLocations(Pos pos1, Pos pos2) const { + return sqrt(pow(pos1.x - pos2.x, 2) + pow(pos1.y - pos2.y, 2)); + } + + /// Sets the level of verbosity for printing errors or throwing exceptions. + /// @param verbosityIn The new level of verbosity to set. + void GameMap::setVerbosity(VERBOSITY verbosityIn) { + verbosity = verbosityIn; + } + + /// Overloaded [Pos] operator enables access in the game map with a Pos. + /// @param pos Position to index into the map. + /// @return A reference to the vector of GameUnits at the location. + vector& GameMap::operator[](const Pos &pos) { + if (!inArenaBounds(pos)) + Util::printError("Out of bounds exception", CRASH, verbosity); + + return map.at(pos.x).at(pos.y); + } + + /// Overloaded const [Pos] operator enables const access in the game map with a Pos. + /// @param pos Position to index into the map. + /// @return A reference to the vector of GameUnits at the location. + const vector& GameMap::operator[](const Pos &pos) const { + if (!inArenaBounds(pos)) + Util::printError("Out of bounds exception", CRASH, verbosity); + + return map.at(pos.x).at(pos.y); + } + + /// Overloaded [int] operator enables access in the game map with [x][y]. + /// @param x X coordinate to index into the map. + /// @return A reference to the column of the game map at coordinate X. + vector>& GameMap::operator[](int x) { + // y = 13 and 14 are true to any valid x value + if (!inArenaBounds(Pos(x, 13))) + Util::printError("Out of bounds exception", CRASH, verbosity); + + return map.at(x); + } + + /// Iterator function to help auto range through the game map. + /// @return A vector begin iterator through the columns of the game map. + vector>>::iterator GameMap::begin() { + return map.begin(); + } + + /// Iterator function to help auto range through the game map. + /// @return A vector end iterator through the columns of the game map. + vector>>::iterator GameMap::end() { + return map.end(); + } + + + /// Prints an ACSII version of the current game map for debug purposes. + /// @return A string ascii representation of the current map. + string GameMap::toString() const { + string ret = ""; + for (unsigned int y = 0; y < ARENA_SIZE; y++) { + for (unsigned int x = 0; x < ARENA_SIZE; x++) { + Pos location = { x, ARENA_SIZE - y - 1 }; + const vector &stack = map.at(location.x).at(location.y); + if (!inArenaBounds(location)) + ret += " "; + else if (stack.size() == 0) + ret += " - "; + else { + ret += unitTypeStr(stack.at(0).unitType) + " "; + } + } + ret += "\n"; + } + return ret; + } + +} diff --git a/community/c++-algo/include/GameLib/src/gameMap.h b/community/c++-algo/include/GameLib/src/gameMap.h new file mode 100644 index 00000000..96b78231 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/gameMap.h @@ -0,0 +1,79 @@ +/* +Description: Contains a representation of the current game map provided by the engine. +Last Modified: 09 Apr 2019 +Author: Isaac Draper, Ryan Draves +*/ + +#ifndef ALGO_MAP_H +#define ALGO_MAP_H + +#include +#include + +#include "json11/json11.hpp" +#include "structs.h" +#include "enums.h" +#include "util.h" +#include "unit.h" +#include "customExceptions.h" + +namespace terminal { + + using json11::Json; + using std::vector; + + /// Holds data about the current game map and provides functions + /// useful for getting information related to the map. + class GameMap { + private: + // Functions + void createEmptyGrid(); + double distanceBetweenLocations(Pos pos_1, Pos pos_2) const; + + public: + // Functions + GameMap(Json config); + + void addUnit(UNIT_TYPE unitType, int x, int y, int playerIndex, double hp = 0); + void addUnit(UNIT_TYPE unitType, Pos pos, int playerIndex, double hp = 0); + void removeUnits(Pos pos); + + void getEdges(vector>& vec) const; + void getEdgeLocations(vector& vec, const EDGE edge) const; + void getLocationsInRange(vector& locations, Pos pos, double radius) const; + + bool inArenaBounds(Pos pos) const; + bool inArenaBounds(int x, int y) const; + + bool containsStationaryUnit(Pos pos) const; + bool containsStationaryUnit(int x, int y) const; + + vector>& operator[](int x); + vector& operator[](const Pos &pos); + const vector& operator[](const Pos &pos) const; + + vector>>::iterator begin(); + vector>>::iterator end(); + + void setVerbosity(VERBOSITY verbosityIn); + string toString() const; + + // Members + const unsigned int ARENA_SIZE = 28; ///< The size of the map. + const unsigned int HALF_ARENA = 14; ///< Half the size of the map. + + Json config; ///< Holds information about the game. + vector>> map; ///< Keeps track of the units at each individual position. + VERBOSITY verbosity; ///< The level at which to print and throw errors. + }; + + /// This sends a representation of the GameMap object to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, GameMap const& gameMap) { + os << gameMap.toString(); + return os; + } + +} + +#endif diff --git a/community/c++-algo/include/GameLib/src/gameState.cpp b/community/c++-algo/include/GameLib/src/gameState.cpp new file mode 100644 index 00000000..cb8a2537 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/gameState.cpp @@ -0,0 +1,429 @@ +/* +Description: Implementations for the gameState header. +Last Modified: 12 Apr 2019 +Author: Isaac Draper +*/ + +#include "gameState.h" + +namespace terminal { + + using json11::Json; + using std::to_string; + using std::string; + using std::vector; + + /// Constructor for GameState which requires a configuration Json object + /// and a Json object representing the current state of the game. + /// @param configuration A Json object containing information about the game. + /// @param currentState A Json object containing information about the current state. + GameState::GameState(Json configuration, Json jsonState) : gameMap(configuration) { + config = configuration; + verbosity = WARNING; + + buildStack = Json::array(); + deployStack = Json::array(); + + parseState(jsonState); + } + + /// Fills in the rest of the required data from the engine. + /// Converts the Json object into data to be used by the class members. + /// @param jsonState A Json object containing the current state of the game. + void GameState::parseState(Json jsonState) { + turnNumber = jsonState["turnInfo"].array_items().at(1).int_value(); + + Json::array p1Stats = jsonState["p1Stats"].array_items(); + Json::array p2Stats = jsonState["p2Stats"].array_items(); + + parsePlayerStats(player1, 0, p1Stats); + parsePlayerStats(player2, 1, p2Stats); + + Json::array p1Units = jsonState["p1Units"].array_items(); + Json::array p2Units = jsonState["p2Units"].array_items(); + + parseUnits(player1, p1Units); + parseUnits(player2, p2Units); + } + + /// Fills in the appropriate information for a player by reference. + /// @param player The player to parse the stats for (by reference). + /// @param stats A Json array containing the stats for the player. + void GameState::parsePlayerStats(Player& player, unsigned int id, Json::array stats) { + player.health = stats.at(0).int_value(); + player.cores = stats.at(1).number_value(); + player.bits = stats.at(2).number_value(); + player.time = stats.at(3).int_value(); + player.id = id; + } + + /// Parses Json units and creates unit structs to be used by the GameState. + /// @param jsonUnits A Json array containing Json arrays of units. + /// @param player The player the units belong to (by reference). + void GameState::parseUnits(Player& player, Json::array jsonUnits) { + int i = 0; + for (Json unitsObj : jsonUnits) { + Json::array unitsRaw = unitsObj.array_items(); + + for (Json unitObj : unitsRaw) { + Json::array unitRaw = unitObj.array_items(); + + UNIT_TYPE unitType = static_cast(i); + unsigned int x = unitRaw.at(0).int_value(); + unsigned int y = unitRaw.at(1).int_value(); + double hp = unitRaw.at(2).number_value(); + + gameMap.addUnit(unitType, x, y, player.id, hp); + } + ++i; + } + } + + /// Sets a resource for a player. This is called internally. + /// @param resourceType The resource type to set. + /// @param amount The new amount to set. + /// @param player The player whos resource to update. + void GameState::setResource(RESOURCE resourceType, double amount, Player& player) { + double heldResource = getResource(resourceType, player); + if (resourceType == BITS) { + player.bits = heldResource + amount; + } + else if (resourceType == CORES) { + player.cores = heldResource + amount; + } + } + + /// Sets a resource for a player. This is called internally. + /// The default player is player 1. + /// @param resourceType The resource type to set. + /// @param amount The new amount to set. + /// @param playerIndex The id of the player to get (0 or 1). + void GameState::setResource(RESOURCE resourceType, double amount, unsigned int playerIndex) { + playerIndex == 0 ? + setResource(resourceType, amount, player1) : + setResource(resourceType, amount, player2); + } + + /// Gets the amount of a resource held by a player. + /// @param resourceType The resource type to get. + /// @param player The player whos resource to get. + /// @return The amount of a resouce a player has. + double GameState::getResource(RESOURCE resourceType, const Player& player) const { + return resourceType == BITS ? player.bits : player.cores; + } + + /// Gets the amount of a resource held by a player. + /// The default is player 1. + /// @param resourceType The resource type to get. + /// @param playerIndex The player whos resource to get. + /// @return The amount of a resouce player1 has. + double GameState::getResource(RESOURCE resourceType, unsigned int playerIndex) const { + return playerIndex == 0 ? + getResource(resourceType, player1) : + getResource(resourceType, player2); + } + + /// Returns the type of resource required to build a unit + /// based on the unit type passed. + /// @param unitType the UNIT_TYPE to get the resource. + /// @return The type of resource the given unit requires. + RESOURCE GameState::resourceRequired(UNIT_TYPE unitType) const { + return isStationary(unitType) ? CORES : BITS; + } + + /// Submits and ends your turn, sending all changes to the engine. + /// Must be called at the end of your turn. + /// This should be called only once per turn. + void GameState::submitTurn() const { + Util::sendCommand(Json(buildStack).dump()); + Util::sendCommand(Json(deployStack).dump()); + } + + /// Checks if a unit type is stationary. + /// @param unitType The unit type to check. + /// @return A bool saying whether it is stationary. + bool GameState::isStationary(UNIT_TYPE unitType) const { + return unitType < 3; + } + + /// Returns a player struct based on the player number passed. + /// @param id The player number. + /// @return A player struct containing information about the player. + Player GameState::getPlayer(int id) const { + return id == 1 ? player1 : player2; + } + + /// Returns the current turn number of the game. + /// @return The current turn number. + unsigned int GameState::getTurn() const { + return turnNumber; + } + + /// The number of units a player can afford. + /// @param unitType The type of unit to check. + /// @param player The player to use. + /// @return The number of units that player can afford. + unsigned int GameState::numberAffordable(UNIT_TYPE unitType, const Player& player) const { + double cost = typeCost(unitType); + RESOURCE resourceType = resourceRequired(unitType); + double playerHeld = getResource(resourceType, player); + + return (unsigned int)(playerHeld / cost); + } + + /// The number of units a player can afford. + /// @param unitType The type of unit to check. + /// @param playerIndex The player to use (0 or 1). + /// @return The number of units that player can afford. + unsigned int GameState::numberAffordable(UNIT_TYPE unitType, unsigned int playerIndex) const { + return playerIndex == 0 ? + numberAffordable(unitType, player1) : + numberAffordable(unitType, player2); + } + + /// Predicts the number of bits a player will have in a future turn. + /// @param turnsInFuture The number of turns to look ahead. + /// @param currentBits Will use this value instead of player's if passed. + /// @param player The player to use. + /// @return The number of bits after so many turns. + double GameState::projectFutureBits(int turnsInFuture, double currentBits, const Player& player) const { + if (turnsInFuture < 1 || turnsInFuture > 99) { + Util::printError("Invalid turns in future used (" + std::to_string(turnsInFuture) + "). Turns in future should be betweeen 1 and 99.", WARNING, verbosity); + } + + double bits = currentBits >= 0 ? currentBits : getResource(BITS, player); + for (int i = 1; i < turnsInFuture + 1; ++i) { + int currentTurn = turnNumber + i; + bits *= (1 - config["resources"]["bitDecayPerRound"].number_value()); + double bitsGained = config["resources"]["bitsPerRound"].number_value() + (int)(currentTurn / config["resources"]["turnIntervalForBitSchedule"].number_value()); + bits += bitsGained; + bits = ((int)(bits * 100 + 0.5)) / 100.0; + } + + return bits; + } + + /// Predicts the number of bits a player will have in a future turn. + /// @param turnsInFuture The number of turns to look ahead. + /// @param currentBits Will use this value instead of player's if passed. + /// @param playerIndex The player to use. + /// @return The number of bits after so many turns. + double GameState::projectFutureBits(int turnsInFuture, double currentBits, unsigned int playerIndex) const { + return playerIndex == 0 ? + projectFutureBits(turnsInFuture, currentBits, player1) : + projectFutureBits(turnsInFuture, currentBits, player2); + } + + /// Gets the cost of a unit based on it's type. + /// @param unitType The type of unit. + /// @return The cost of the unit. + double GameState::typeCost(UNIT_TYPE unitType) const { + return config["unitInformation"].array_items().at(unitType)["cost"].number_value(); + } + + /// Checks if we can spawn a unit at a given location. + /// @param unitType The type of unit to check. + /// @param x The x position to check. + /// @param y The y position to check. + /// @param num The number of units to check, default is 1. + /// @return A bool, true if can spawn. + bool GameState::canSpawn(UNIT_TYPE unitType, unsigned int x, unsigned int y, unsigned int num) const { + if (!gameMap.inArenaBounds(x, y)) { + Util::printError("Out of bounds exception", CRASH, verbosity); + return false; + } + + const bool affordable = numberAffordable(unitType, player1) >= num; + const bool stationary = isStationary(unitType); + const bool blocked = gameMap.containsStationaryUnit(x, y) || + (stationary && gameMap[Pos(x, y)].size() > 0); + const bool correctTerritory = y < gameMap.HALF_ARENA; + + vector edges; + gameMap.getEdgeLocations(edges, BOTTOM_LEFT); + gameMap.getEdgeLocations(edges, BOTTOM_RIGHT); + const bool onEdge = std::find(edges.begin(), edges.end(), Pos(x, y)) != edges.end(); + + string failReason = ""; + if (!affordable) + failReason += " Not enough resources."; + if (blocked) + failReason += " Location is blocked."; + if (!correctTerritory) + failReason += " Location in enemy territory."; + if (!(stationary || onEdge)) + failReason += " Information units must be deployed on the edge."; + if (failReason.length() > 0) + Util::printError("Could not spawn " + + unitTypeStr(unitType) + " at location " + + "(" + to_string(x) + ", " + to_string(y) + "). " + + failReason, + WARNING, verbosity); + + return (affordable && correctTerritory && !blocked && (stationary || onEdge) && (!stationary || num == 1)); + } + + /// Checks if we can spawn a unit at a given location. + /// @param unitType The type of unit to check. + /// @param pos The position to check. + /// @param num The number of units to check, default is 1. + /// @return A bool, true if can spawn. + bool GameState::canSpawn(UNIT_TYPE unitType, Pos pos, unsigned int num) const { + return canSpawn(unitType, pos.x, pos.y, num); + } + + /// Attempts to spawn new units with the type give at a single location. + /// @param unitType The type of unit to spawn. + /// @param x The x location to spawn the unit. + /// @param y The y location to spawn the unit. + /// @param num The number of units to spawn. + /// @return Returns the number of units spawned (0 or 1). + unsigned int GameState::attemptSpawn(UNIT_TYPE unitType, unsigned int x, unsigned int y, int num) { + if (canSpawn(unitType, x, y)) { + int cost = (int)typeCost(unitType); + RESOURCE resourceType = resourceRequired(unitType); + setResource(resourceType, 0 - cost); + + gameMap.addUnit(unitType, x, y, 0); + + if (isStationary(unitType)) + buildStack.push_back({ Json::array({ unitTypeStr(unitType), (int)x, (int)y }) }); + else + deployStack.push_back({ Json::array({ unitTypeStr(unitType), (int)x, (int)y }) }); + + return 1; + } + else { + Util::printError("Tried to spawn unit at (" + to_string(x) + ", " + to_string(y) + ") but could not.", WARNING, verbosity); + } + + return 0; + } + + /// Attempts to spawn new units with the type give at a single location. + /// @param unitType The type of unit to spawn. + /// @param pos The location to spawn the unit. + /// @param num The number of units to spawn. + /// @return Returns the number of units spawned (0 or 1). + unsigned int GameState::attemptSpawn(UNIT_TYPE unitType, Pos pos, int num) { + return attemptSpawn(unitType, pos.x, pos.y); + } + + /// Attempts to spawn units with the type give at a list of locations. + /// @param unitType The type of unit to spawn. + /// @param locations A vector of locations to spawn the units. + /// @param num The numer of units to spawn at each location (default is 1). + /// @return Returns the number of units spawned. + unsigned int GameState::attemptSpawn(UNIT_TYPE unitType, vector& locations, int num) { + if (num < 1) { + Util::printError("Attempted to spawn fewer than one unit.", WARNING, verbosity); + } + + unsigned int numSpawned = 0; + for (Pos pos : locations) { + numSpawned += attemptSpawn(unitType, pos); + } + + return numSpawned; + } + + /// Attempts to remove existing friendly firewalls in a given location. + /// @param x The x location to try and remove. + /// @param y The y location to try and remove. + /// @return Returns the number of units removed (0 or 1). + unsigned int GameState::attemptRemove(unsigned int x, unsigned int y) { + if (y < gameMap.HALF_ARENA && + gameMap.containsStationaryUnit(Pos(x, y))) { + buildStack.push_back({ Json::array({ unitTypeStr(REMOVE), (int)x, (int)y}) }); + + return 1; + } + else { + Util::printError("Could not remove a unit from (" + to_string(x) + ", " + to_string(y) + "). Location has no firewall or it is in enemy territory", WARNING, verbosity); + } + + return 0; + } + + /// Attempts to remove existing friendly firewalls in a given location. + /// @param pos The location to try and remove. + /// @return Returns the number of units removed (0 or 1). + unsigned int GameState::attemptRemove(Pos pos) { + return attemptRemove(pos.x, pos.y); + } + + /// Attempts to remove existing friendly firewalls at each position in a vector. + /// @param locations The locations to try and remove. + /// @return Returns the number of units removed. + unsigned int GameState::attemptRemove(vector& locations) { + unsigned int numRemoved = 0; + for (Pos pos : locations) { + numRemoved += attemptRemove(pos); + } + + return numRemoved; + } + + /// Gets the path a unit would take. + /// Fills a given vector with the positions of a path. + /// @param path The vector to fill with the path. + /// @param startLocation The start position for the unit. + /// @param targetEdge The edge to try and reach. + void GameState::findPathToEdge(vector& path, Pos startLocation, EDGE targetEdge) { + if (gameMap.containsStationaryUnit(startLocation)) + Util::printError("Attempted to perform pathing from blocked starting location (" + to_string(startLocation.x) + ", " + to_string(startLocation.y) + ").", WARNING, verbosity); + + ShortestPathFinder shortestPathFinder = ShortestPathFinder(gameMap); + vector endPoints; + gameMap.getEdgeLocations(endPoints, targetEdge); + shortestPathFinder.navigateMultipleEndpoints(startLocation, endPoints, path); + } + + /// Gets the path a unit would take. + /// Fills a given vector with the positions of a path. + /// @param path The vector to fill with the path. + /// @param x The x position for the unit. + /// @param y The y position for the unit. + /// @param targetEdge The edge to try and reach. + void GameState::findPathToEdge(vector& path, unsigned int x, unsigned int y, EDGE targetEdge) { + return findPathToEdge(path, Pos(x, y), targetEdge); + } + + /// Sets a new verbosity level. This determines whether errors will + /// be ignored, printed, or throw exceptions. + /// This also sets the verbosity of the GameMap. + /// @param newVerbosity The new error level. + void GameState::setVerbosity(VERBOSITY newVerbosity) { + verbosity = newVerbosity; + gameMap.setVerbosity(newVerbosity); + } + + /// Sets the verbosity level to SUPPRESS. + /// This causes all errors to be ignored. + /// This also sets the verbosity of the GameMap. + void GameState::suppressWarnings() { + verbosity = SUPPRESS; + gameMap.setVerbosity(SUPPRESS); + } + + /// This returns a string representation of the GameState object. + /// @return A string to represent this object. + string GameState::toString() const { + std::stringstream ss; + ss.precision(1); + ss << std::fixed; + + ss << "GameState:\n\t\t\tBits\tCores\tHealth\n\t" << + "Player 1:\t" << + player1.bits << "\t" << + player1.cores << "\t" << + player1.health << "\n\t" << + "Player 2:\t" << + player2.bits << "\t" << + player2.cores << "\t" << + player2.health << "\n"; + + return ss.str(); + } + +} diff --git a/community/c++-algo/include/GameLib/src/gameState.h b/community/c++-algo/include/GameLib/src/gameState.h new file mode 100644 index 00000000..925954d2 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/gameState.h @@ -0,0 +1,109 @@ +/* +Description: Represents the current state of the game. +Last Modified: 09 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef GAME_STATE_H +#define GAME_STATE_H + +#include +#include +#include +#include +#include + +#include "json11/json11.hpp" +#include "customExceptions.h" +#include "navigation.h" +#include "structs.h" +#include "gameMap.h" +#include "structs.h" +#include "enums.h" +#include "util.h" + +namespace terminal { + + using json11::Json; + using std::string; + using std::vector; + + /// This represents everything about a current state of the game. + /// It provides a convienent way to add/remove units and + /// get information about the opponent. + class GameState { + public: + // Functions + GameState(Json configuration, Json jsonState); + void submitTurn() const; + + unsigned int attemptSpawn(UNIT_TYPE unitType, Pos pos, int num = 1); + unsigned int attemptSpawn(UNIT_TYPE unitType, vector& locations, int num = 1); + unsigned int attemptSpawn(UNIT_TYPE unitType, unsigned int x, unsigned int y, int num = 1); + + unsigned int attemptRemove(Pos pos); + unsigned int attemptRemove(vector& locations); + unsigned int attemptRemove(unsigned int x, unsigned int y); + + bool canSpawn(UNIT_TYPE unitType, const Pos pos, unsigned int num = 1) const; + bool canSpawn(UNIT_TYPE unitType, unsigned int x, unsigned int y, unsigned int num = 1) const; + + unsigned int numberAffordable(UNIT_TYPE unitType, const Player& player) const; + unsigned int numberAffordable(UNIT_TYPE unitType, unsigned int playerIndex = 0) const; + + double projectFutureBits(int turnsInFuture, double currentBits, const Player& player) const; + double projectFutureBits(int turnsInFuture = 1, double currentBits = -1, unsigned int playerIndex = 0) const; + + double getResource(RESOURCE resourceType, const Player& player) const; + double getResource(RESOURCE resourceType, unsigned int playerIndex = 0) const; + + double typeCost(UNIT_TYPE unitType) const; + RESOURCE resourceRequired(UNIT_TYPE unitType) const; + + void findPathToEdge(vector& path, Pos startLocation, EDGE targetEdge); + void findPathToEdge(vector& path, unsigned int x, unsigned int y, EDGE targetEdge); + + bool isStationary(UNIT_TYPE unitType) const; + Player getPlayer(int id) const; + unsigned int getTurn() const; + + void setVerbosity(VERBOSITY newErrorLevel); + void suppressWarnings(); + + virtual string toString() const; + + // Members + GameMap gameMap; ///< Holds the game map associated with this state. + + private: + // Functions + void parseState(Json jsonState); + void parseUnits(Player& player, Json::array jsonUnits); + void parsePlayerStats(Player& player, unsigned int id, Json::array stats); + + void setResource(RESOURCE resourceType, double amount, Player& player); + void setResource(RESOURCE resourceType, double amount, unsigned int playerIndex = 0); + + // Members + Json config; ///< Holds information about the game. + + Player player1; ///< Holds stats for the first player. + Player player2; ///< Holds stats for the second player. + + Json::array buildStack; ///< The static units we will send to the engine to spawn. + Json::array deployStack; ///< The mobile units we will send to the engine to spawn. + + unsigned int turnNumber; ///< The current turn number. + VERBOSITY verbosity; ///< The level at which to print and throw errors. + }; + + /// This sends a representation of the GameState object to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, GameState const& gameState) { + os << gameState.toString(); + return os; + } + +} + +#endif \ No newline at end of file diff --git a/community/c++-algo/include/GameLib/src/navigation.cpp b/community/c++-algo/include/GameLib/src/navigation.cpp new file mode 100644 index 00000000..25c50f7a --- /dev/null +++ b/community/c++-algo/include/GameLib/src/navigation.cpp @@ -0,0 +1,323 @@ +/* +Description: Implementations for the navigation header. +Last Modified: 08 Apr 2019 +Author: Patrik Dobias +*/ + +#include "navigation.h" + +namespace terminal { + + using std::numeric_limits; + using std::find; + using std::vector; + using std::queue; + using std::cerr; + + /// Constructor for ShortestPathFinder. + /// @param gameMap A GameMap object representing the current game map (by reference). + ShortestPathFinder::ShortestPathFinder(GameMap& gameMap) : gameMap(gameMap) { + initialized = false; + } + + /// Initializes the navigation map with nodes. + /// We cannot run pathfinding without this being called first. + void ShortestPathFinder::initializeMap() { + initialized = true; + nodeMap.reserve(gameMap.ARENA_SIZE); + + for (unsigned int i = 0; i < gameMap.ARENA_SIZE; i++) { + nodeMap.push_back(vector()); + nodeMap.at(i).reserve(gameMap.ARENA_SIZE); + for (unsigned int j = 0; j < gameMap.ARENA_SIZE; j++) { + nodeMap.at(i).push_back(Node()); + } + } + } + + /// Finds the path a unit would take to reach a set of endpoints. + /// @param startPoint The starting location of the unit. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @param path An empty vector, which will be filled with path (by reference). + void ShortestPathFinder::navigateMultipleEndpoints(Pos startPoint, const vector& endPoints, vector& path) { + if (gameMap.containsStationaryUnit(startPoint)) + return; + + initializeMap(); + + for (unsigned int x = 0; x < gameMap.ARENA_SIZE; x++) + for (unsigned int y = 0; y < gameMap.ARENA_SIZE; y++) + if (gameMap.containsStationaryUnit(x, y)) + nodeMap.at(x).at(y).blocked = true; + + Pos idealEndpoint = idealnessSearch(startPoint, endPoints); + + validate(idealEndpoint, endPoints); + + getPath(startPoint, endPoints, path); + } + + /// Finds the most ideal endPoints. + /// @param startPoint The starting location of the Unit. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @return Most ideal end point (location at target edge or self destruct location). + Pos ShortestPathFinder::idealnessSearch(Pos startPoint, const vector& endPoints) { + queue current; + current.push(startPoint); + int bestIdealness = getIdealness(startPoint, endPoints); + nodeMap.at(startPoint.x).at(startPoint.y).visitedIdealness = true; + Pos mostIdeal = startPoint; + + while(!current.empty()) { + Pos searchLocation = current.front(); + current.pop(); + + vector neighbors; + getNeighbors(searchLocation, neighbors); + for(auto neighbor: neighbors) { + int x = neighbor.x; + int y = neighbor.y; + + if(!gameMap.inArenaBounds(neighbor) || nodeMap.at(x).at(y).blocked) + continue; + + int currentIdealness = getIdealness(neighbor, endPoints); + + if(currentIdealness > bestIdealness) { + bestIdealness = currentIdealness; + mostIdeal = neighbor; + } + + if(!nodeMap.at(x).at(y).visitedIdealness) { + nodeMap.at(x).at(y).visitedIdealness = true; + current.push(neighbor); + } + } + } + + return mostIdeal; + } + + /// Gets locations adjacent to a location. + /// @param location The location to get it's neighbors. + /// @param neighbors An empty vector, which will be filled with neighbor locations (by reference). + void ShortestPathFinder::getNeighbors(Pos location, vector& neighbors) { + if (location.y < gameMap.ARENA_SIZE) + neighbors.push_back({location.x , location.y + 1}); + if (location.y > 0) + neighbors.push_back({location.x , location.y - 1}); + if (location.x < gameMap.ARENA_SIZE) + neighbors.push_back({location.x + 1, location.y }); + if (location.x > 0) + neighbors.push_back({ location.x - 1, location.y }); + } + + /// Gets direction for given endPoints. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @return A direction [x, y] representing the edge. + Pos ShortestPathFinder::getDirectionFromEndPoints(const vector& endPoints) { + Pos direction = {1, 1}; + Pos point = endPoints[0]; + + if(point.x < gameMap.HALF_ARENA) + direction.x = -1; + if(point.y < gameMap.HALF_ARENA) + direction.y = -1; + + return direction; + } + + /// Gets the idealness of a tile, the reachable tile the unit most wants to path to. + /// Better self destruct locations are more ideal. The endpoints are perfectly ideal. + /// @param location A location of the tile. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @return Idealness value of the location. + int ShortestPathFinder::getIdealness(Pos location, const vector& endPoints) { + if(find(endPoints.begin(), endPoints.end(), location) != endPoints.end()) + return numeric_limits::max(); + + Pos direction = getDirectionFromEndPoints(endPoints); + int idealness = 0; + + if(direction.y == 1) + idealness += 28 * location.y; + else + idealness += 28 * (27 - location.y); + if(direction.x == 1) + idealness += location.x; + else + idealness += (27 - location.x); + + return idealness; + } + + /// Breadth first search of the grid, setting the pathlengths of each node. + /// @param idealTile A best location, where path can end. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + void ShortestPathFinder::validate(Pos idealTile, const vector& endPoints) { + queue current; + + if(find(endPoints.begin(), endPoints.end(), idealTile) != endPoints.end()) { + for(auto location: endPoints) { + current.push(location); + nodeMap.at(location.x).at(location.y).pathLength = 0; + nodeMap.at(location.x).at(location.y).visitedValidate = true; + } + } else { + current.push(idealTile); + nodeMap.at(idealTile.x).at(idealTile.y).pathLength = 0; + nodeMap.at(idealTile.x).at(idealTile.y).visitedValidate = true; + } + + while(!current.empty()) { + Pos currentLocation = current.front(); + current.pop(); + Node currentNode = nodeMap.at(currentLocation.x).at(currentLocation.y); + + vector neighbors; + getNeighbors(currentLocation, neighbors); + for(auto neighbor: neighbors) { + int x = neighbor.x; + int y = neighbor.y; + + if(!gameMap.inArenaBounds(neighbor) || nodeMap.at(x).at(y).blocked) + continue; + + if(!nodeMap.at(x).at(y).visitedValidate && !currentNode.blocked) { + nodeMap.at(x).at(y).pathLength = currentNode.pathLength + 1; + nodeMap.at(x).at(y).visitedValidate = true; + current.push(neighbor); + } + } + } + } + + /// Gets path from startPoint to one of the endPoints, based on values of each tile. + /// @param startPoint The starting location of the unit. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @param path An empty vector, which will be filled with path (by reference). + void ShortestPathFinder::getPath(Pos startPoint, const vector& endPoints, vector& path) { + path.push_back(startPoint); + Pos current = startPoint; + MOVE_DIRECTION moveDirection = NONE; + + while(nodeMap.at(current.x).at(current.y).pathLength != 0) { + Pos nextMove = chooseNextMove(current, moveDirection, endPoints); + + if(current.x == nextMove.x) + moveDirection = VERTICAL; + else + moveDirection = HORIZONTAL; + + path.push_back(nextMove); + current = nextMove; + } + } + + /// Gets best 'next step' from the current location. + /// @param currentPoint A current location. + /// @param previousMoveDirection Represents wheter previous move was vertical or horizontal. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @return Next position from currentPoint. + Pos ShortestPathFinder::chooseNextMove(Pos currentPoint, MOVE_DIRECTION previousMoveDirection, const vector& endPoints) { + vector neighbors; + getNeighbors(currentPoint, neighbors); + + Pos idealNeighbor = currentPoint; + int bestPathLength = nodeMap.at(currentPoint.x).at(currentPoint.y).pathLength; + + for(auto neighbor: neighbors) { + int x = neighbor.x; + int y = neighbor.y; + + if(!gameMap.inArenaBounds(neighbor) || nodeMap.at(x).at(y).blocked) + continue; + + bool newBest = false; + int currentPathLength = nodeMap.at(x).at(y).pathLength; + + if(currentPathLength > bestPathLength) + continue; + else if(currentPathLength < bestPathLength) + newBest = true; + + if(!newBest && !betterDirection(currentPoint, neighbor, idealNeighbor, previousMoveDirection, endPoints)) + continue; + + idealNeighbor = neighbor; + bestPathLength = currentPathLength; + } + + return idealNeighbor; + } + + /// Check whether newTile should be used in path or prevBest according to previou move. + /// Should prioritize zig zag path. + /// @param prevTile Current location of path. + /// @param newTile Possible next location. + /// @param prevBest Possible previous best next location. + /// @param previousMoveDirection Represents wheter previous move was vertical or horizontal. + /// @param endPoints A vector of end locations for the unit, should be a list of edge locations (by reference). + /// @return Whether the unit would rather move to the new one. + bool ShortestPathFinder::betterDirection(Pos prevTile, Pos newTile, Pos prevBest, MOVE_DIRECTION previousMoveDirection, const vector& endPoints) { + if(previousMoveDirection == HORIZONTAL && newTile.x != prevBest.x) { + if(prevTile.y == newTile.y) + return false; + return true; + } + if(previousMoveDirection == VERTICAL && newTile.y != prevBest.y) { + if(prevTile.x == newTile.x) + return false; + return true; + } + if(previousMoveDirection == NONE) { + if(prevTile.y == newTile.y) + return false; + return true; + } + + Pos direction = getDirectionFromEndPoints(endPoints); + if(newTile.y == prevBest.y) { + if(direction.x == 1 && newTile.x > prevBest.x) + return true; + if(direction.x == -1 && newTile.x < prevBest.x) + return true; + return false; + } + if(newTile.x == prevBest.x) { + if(direction.y == 1 && newTile.y > prevBest.y) + return true; + if(direction.y == -1 && newTile.y < prevBest.y) + return true; + return false; + } + return true; + } + + /// Prints an ASCII version of the current node map for debug purposes. + /// It prints the length of each node from the start position. + void ShortestPathFinder::printMap() { + if(!initialized) { + Util::debugWrite("Attempted to print_map before pathfinder initialization. Use 'this_object.initialize_map(game_state)' to initialize the map first"); + return; + } + + for(unsigned int y = 0; y < gameMap.ARENA_SIZE; y++) { + for(unsigned int x = 0; x < gameMap.ARENA_SIZE; x++) { + Node node = nodeMap.at(x).at(28 - y - 1); + if(!node.blocked && node.pathLength != -1) + printJustified(node.pathLength); + else + cerr << " "; + } + Util::debugWrite(""); + } + } + + /// Prints a number between 100 and -10 in 3 spaces. + void ShortestPathFinder::printJustified(int number) { + if(number < 10 && number > -1) + cerr << " "; + cerr << number << " "; + } +} \ No newline at end of file diff --git a/community/c++-algo/include/GameLib/src/navigation.h b/community/c++-algo/include/GameLib/src/navigation.h new file mode 100644 index 00000000..b9df5f3d --- /dev/null +++ b/community/c++-algo/include/GameLib/src/navigation.h @@ -0,0 +1,75 @@ +/* +Description: Runs pathfinding using terminal logic. +Last Modified: 08 Apr 2019 +Author: Patrik Dobias +*/ + +#ifndef NAVIGATION_H +#define NAVIGATION_H + +#include +#include +#include +#include + +#include "util.h" +#include "structs.h" +#include "gameMap.h" + +namespace terminal { + + using std::vector; + + /// Represents the last direction moved by a unit. + enum MOVE_DIRECTION { + NONE, ///< Means the unit has not moved yet. + HORIZONTAL, ///< The unit moved horizontally last. + VERTICAL ///< The unit moved vertically last. + }; + + /// Representats a location in the games map for pathfinding. + struct Node { + bool visitedIdealness; ///< How good this node is to move towards the target point. + bool visitedValidate; ///< Whether this node has been validated. + bool blocked; ///< Whether this node is blocked. + int pathLength; ///< The number of steps to get to this node. + + /// Constructor for creating a Node. + Node(): visitedIdealness(false), visitedValidate(false), blocked(false), pathLength(-1) { } + }; + + /// This class finds the shortest path from a given + /// start and end point. If one does not exists it + /// returns the best path using terminal's pathing logic. + class ShortestPathFinder { + public: + // Functions + ShortestPathFinder(GameMap& gameMap); + void navigateMultipleEndpoints(Pos startPoint, const vector& endPoints, vector& path); + void printMap(); + + private: + // Functions + void initializeMap(); + Pos idealnessSearch(Pos startPoint, const vector& endPoints); + + void getNeighbors(Pos location, vector& neighbors); + Pos getDirectionFromEndPoints(const vector& endPoints); + int getIdealness(Pos location, const vector& endPoints); + + void validate(Pos idealTile, const vector& endPoints); + void getPath(Pos startPoint, const vector& endPoints, vector& path); + + Pos chooseNextMove(Pos currentPoint, MOVE_DIRECTION previousMoveDirection, const vector& endPoints); + bool betterDirection(Pos prevTile, Pos newTile, Pos prevBest, MOVE_DIRECTION previousMoveDirection, const vector& endPoints); + void printJustified(int number); + + // Members + GameMap gameMap; ///< The map for the current state of the game. + bool initialized; ///< Whether or not the node map has been created. + vector> nodeMap; ///< Represents a terminal map for pathfinding only. + }; + +} + +#endif \ No newline at end of file diff --git a/community/c++-algo/include/GameLib/src/structs.h b/community/c++-algo/include/GameLib/src/structs.h new file mode 100644 index 00000000..3fb1e5d5 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/structs.h @@ -0,0 +1,78 @@ +/* +Description: Contains useful structs for storing game data. +Last Modified: 08 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef STRUCTS_H +#define STRUCTS_H + +#include + +#include "enums.h" +#include "customExceptions.h" + +namespace terminal { + + /// This represents a position on the board. + /// It requires the positions be positive since + /// the terminal map only has positive points. + struct Pos { + unsigned int x; ///< The x position of this location. + unsigned int y; ///< The y position of this location. + + /// Simple constructor to create the position. + /// @param x X coordinate to set. + /// @param y Y coordinate to set. + Pos(unsigned int x, unsigned int y) : x(x), y(y) {} + + /// This allows for python location indexing to be transferrable. + /// For example, to get the x position you can do: pos[0] + /// @param index The index of the position coordinate. + /// @return The value of the coordinate. + inline int operator[](unsigned int index) { + if (index == 0) return x; + if (index == 1) return y; + throw PosException("Invalid position index (0 or 1)"); + return 0; + } + + /// This allows for comparison between positions. + /// Two positions are equal if they have the same x and y values. + /// @param rhs Right hand side position. + /// @return Whether two positions are the same. + inline bool operator==(const Pos &rhs) const { + return x == rhs.x && y == rhs.y; + } + + }; + + /// This represents a player and stores its data. + struct Player { + double bits; ///< The number of bits a player has. + double cores; ///< The number of cores a player has. + int health; ///< The health of a player. + int time; ///< The time a player took for their turn. + int id; ///< The player index, or number. + }; + + /// This sends a representation of the Pos struct to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, Pos const& pos) { + os << "(" << pos.x << ", " << pos.y << ")"; + return os; + } + + /// This sends a representation of the Player struct to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, Player const& player) { + os << "Player " << player.id << ": Bits(" << + player.bits << ") Cores(" << + player.cores << ") Health(" << + player.health << ")"; + return os; + } + +} + +#endif diff --git a/community/c++-algo/include/GameLib/src/unit.cpp b/community/c++-algo/include/GameLib/src/unit.cpp new file mode 100644 index 00000000..767f95d8 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/unit.cpp @@ -0,0 +1,72 @@ +/* +Description: Implmentations for the unit header. +Last Modified: 07 Apr 2019 +Author: Ryan Draves +*/ + +#include "unit.h" + +namespace terminal { + + using std::string; + using std::to_string; + using json11::Json; + + /// Init the passed in params and pass off config initialization to serializeType(). + /// @param unitType Unit type of the game unit. + /// @param config JSON game configuration passed in upon startup. + /// @param stability Health of the game unit. 0 defaults it to maxStability. + /// @param playerIndex 0 for ally unit (you), 1 for enemy unit. + /// @param x X coordinate of the game unit's location. + /// @param y Y coordinate of the game unit's location. + GameUnit::GameUnit(UNIT_TYPE unitType, const Json &config, double stability, int playerIndex, + int x, int y) : unitType(unitType), config(config), playerIndex(playerIndex), + stability(stability), x(x), y(y), pendingRemoval(false) { + serializeType(); + this->stability = stability == 0 ? maxStability : stability; + } + + /// Finish the game unit initialization based on the config. + void GameUnit::serializeType() { + stationary = isStationary(unitType); + Json type_config = config["unitInformation"].array_items().at((int)unitType); + + if (stationary) { + speed = 0; + if (unitType == REMOVE) { + pendingRemoval = true; + // Initialize these in case someone decides to access them. + damage = damageF = damageI = cost = 0; + maxStability = range = 0.0; + return; + } + else if (unitType == ENCRYPTOR) { + damage = type_config["shieldAmount"].int_value(); + } + else { + damage = type_config["damage"].int_value(); + } + damageF = damage; + damageI = damage; + } + else { + speed = type_config["speed"].number_value(); + damageF = type_config["damageF"].int_value(); + damageI = type_config["damageI"].int_value(); + } + + range = type_config["range"].number_value(); + maxStability = type_config["stability"].number_value(); + cost = type_config["cost"].int_value(); + } + + /// Get a string representation of the game unit. + /// @return Elegant string representation of the game unit. + string GameUnit::toString() const { + string owner = playerIndex == 0 ? "Friendly" : "Enemy"; + string removal = pendingRemoval ? ", pending removal" : ""; + return owner + " " + unitTypeStr(unitType) + ", stability: " + + to_string(stability) + " location: [" + to_string(x) + ", " + + to_string(y) + "]" + removal; + } +} diff --git a/community/c++-algo/include/GameLib/src/unit.h b/community/c++-algo/include/GameLib/src/unit.h new file mode 100644 index 00000000..415f3986 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/unit.h @@ -0,0 +1,112 @@ +/* +Description: Holds the representation of a GameUnit. +Last Modified: 07 Apr 2019 +Author: Ryan Draves +*/ + +#ifndef UNIT_H +#define UNIT_H + +#include +#include +#include "enums.h" +#include "customExceptions.h" + +#include "json11/json11.hpp" + +namespace terminal { + + using std::string; + using json11::Json; + + /// Static function to say whether a unit type is stationary. + /// @param unitType Unit type to check. + /// @return boolean Whether the unit type is stationary. + static bool isStationary(const UNIT_TYPE &unitType) { + if (unitType == FILTER || + unitType == ENCRYPTOR || + unitType == DESTRUCTOR) + return true; + return false; + } + + /// Static function to get the string shorthand of a game unit. + /// @param unitType Unit type to check. + /// @return string Shorthand of the unit in string form. + static string unitTypeStr(const UNIT_TYPE &unitType) { + switch (unitType) { + case FILTER: + return "FF"; + break; + case ENCRYPTOR: + return "EF"; + break; + case DESTRUCTOR: + return "DF"; + break; + case PING: + return "PI"; + break; + case EMP: + return "EI"; + break; + case SCRAMBLER: + return "SI"; + break; + case REMOVE: + return "RM"; + break; + default: + throw UnitTypeException(); + } + throw UnitTypeException(); + return ""; + } + + /// A class to represent a unit from terminal. + /// This keeps track of that units health, type, + /// etc as given by the engine. + class GameUnit { + public: + // Functions + GameUnit(UNIT_TYPE unitType, const Json &config, double stability, int playerIndex = 0, + int x = -1, int y = -1); + string toString() const; + + // Members + Json config; ///< Holds information about the game. + + UNIT_TYPE unitType; ///< The type of this unit. + int playerIndex; ///< Which player it belongs to. + double stability; ///< The health of this unit. + int x; ///< The x position of its location. + int y; ///< The y position of its location. + + // Determines these members from the config. + bool stationary; ///< Whether the unit is a firewall. + double speed; ///< How often this unit moves (0 if static). + int damage; ///< The amount of damage this unit does. + int damageF; ///< The damage done to firewalls. + int damageI; ///< The damage done to information units. + double range; ///< How far away this unit can attack. + double maxStability; ///< The maximum amound of health it can have. + int cost; ///< How much it costs to spawn. + bool pendingRemoval; ///< If it has been attempted to be removed. + + private: + // Functions + void serializeType(); + + }; + + /// This sends a representation of the GameUnit object to a stream. + /// @return The stream passed to the function. + inline std::ostream& operator<<(std::ostream& os, GameUnit const& gameUnit) { + os << gameUnit.toString(); + return os; + } + +} + + +#endif diff --git a/community/c++-algo/include/GameLib/src/util.cpp b/community/c++-algo/include/GameLib/src/util.cpp new file mode 100644 index 00000000..3d252fa9 --- /dev/null +++ b/community/c++-algo/include/GameLib/src/util.cpp @@ -0,0 +1,44 @@ +/* +Description: Implmentations for the util header. +Last Modified: 08 Apr 2019 +Author: Isaac Draper +*/ + +#include "util.h" + +namespace terminal { + + using std::cin; + using std::cout; + using std::cerr; + using std::endl; + using std::string; + using json11::Json; + + /// This gets the next command from the terminal engine through cin. + /// It then attempts to parse the command to return a Json object. + /// @return Json object of command from the engine. + Json Util::getCommand() { + string stateStr, err; + cin >> stateStr; + + Json state = Json::parse(stateStr, err); + + if (err != "") { + string errorMsg = "Error parsing string from engine:\n\t" + err; + throw UtilException(errorMsg.c_str()); + } + + return state; + } + + /// This sends a command to the terminal engine through cout. + /// It also removes all newline characters already inside the string. + /// @param command A Json formatted string to send to the engine. + void Util::sendCommand(string command) { + command.erase(std::remove(command.begin(), command.end(), '\n'), command.end()); + cout << command << endl; + cout.flush(); + } + +} diff --git a/community/c++-algo/include/GameLib/src/util.h b/community/c++-algo/include/GameLib/src/util.h new file mode 100644 index 00000000..4009598c --- /dev/null +++ b/community/c++-algo/include/GameLib/src/util.h @@ -0,0 +1,63 @@ +/* +Description: Handles basic utility functions like communicating with the engine. +Last Modified: 08 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include + +#include "json11/json11.hpp" +#include "customExceptions.h" +#include "enums.h" + +namespace terminal { + + using std::string; + using std::cerr; + + /// Handles communication with the engine and other basic functions. + /// All functions are static and should not be const. + /// This class should not be instantiated. + class Util { + public: + // Functions + static json11::Json getCommand(); + static void sendCommand(string command); + + /// This prints a message to the game's debug console using cerr. + /// @param obj An object to print. This is often a string. + /// @param newline Whether or not to print a newline at the end of the message. + template + static void debugWrite(T obj, bool newline=true) { + cerr << obj << (newline ? "\n" : ""); + cerr.flush(); + } + + /// This prints or thows an error depending on the level you set it at. + /// INVARIANT and CRASH levels will throw an exception, + /// WARNING simply prints the error, and + /// SUPPRESS hides all errors. + /// This is a template function where you specify the type + /// of exception (must inhert from CustomException) to throw. + /// @param warning The warning to either print or throw. + /// @param warningLevel The level at which to throw this type of warning. + /// @param currentLevel The level the user currently has it set at. + template + static void printError(std::string warning, VERBOSITY warningLevel, VERBOSITY currentLevel) { + if (currentLevel >= warningLevel) { + if (currentLevel >= INVARIANT) + throw CE(warning); + else + debugWrite(warning); + } + } + }; + +} + +#endif diff --git a/community/c++-algo/include/json11/.gitignore b/community/c++-algo/include/json11/.gitignore new file mode 100644 index 00000000..e959b6fc --- /dev/null +++ b/community/c++-algo/include/json11/.gitignore @@ -0,0 +1,12 @@ +# generated files +test +libjson11.a +json11.pc + +# Cmake +CMakeCache.txt +CTestTestfile.cmake +CMakeFiles +CMakeScripts +cmake_install.cmake +install_manifest.txt \ No newline at end of file diff --git a/community/c++-algo/include/json11/CMakeLists.txt b/community/c++-algo/include/json11/CMakeLists.txt new file mode 100644 index 00000000..819c36f9 --- /dev/null +++ b/community/c++-algo/include/json11/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 2.8) +if (CMAKE_VERSION VERSION_LESS "3") + project(json11 CXX) +else() + cmake_policy(SET CMP0048 NEW) + project(json11 VERSION 1.0.0 LANGUAGES CXX) +endif() + +enable_testing() + +option(JSON11_BUILD_TESTS "Build unit tests" OFF) +option(JSON11_ENABLE_DR1467_CANARY "Enable canary test for DR 1467" OFF) + +if(CMAKE_VERSION VERSION_LESS "3") + add_definitions(-std=c++11) +else() + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX /usr) +endif() + +add_library(json11 json11.cpp) +target_include_directories(json11 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_options(json11 + PRIVATE -fPIC -fno-rtti -fno-exceptions -Wall) + +# Set warning flags, which may vary per platform +include(CheckCXXCompilerFlag) +set(_possible_warnings_flags /W4 /WX -Wextra -Werror) +foreach(_warning_flag in ${_possible_warnings_flags}) + CHECK_CXX_COMPILER_FLAG(_warning_flag _flag_supported) + if(${_flag_supported}) + target_compile_options(json11 PRIVATE ${_warning_flag}) + endif() +endforeach() + +configure_file("json11.pc.in" "json11.pc" @ONLY) + +if (JSON11_BUILD_TESTS) + + # enable test for DR1467, described here: https://llvm.org/bugs/show_bug.cgi?id=23812 + if(JSON11_ENABLE_DR1467_CANARY) + add_definitions(-D JSON11_ENABLE_DR1467_CANARY=1) + else() + add_definitions(-D JSON11_ENABLE_DR1467_CANARY=0) + endif() + + add_executable(json11_test test.cpp) + target_link_libraries(json11_test json11) +endif() + +install(TARGETS json11 DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}) +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/json11.hpp" DESTINATION include/${CMAKE_LIBRARY_ARCHITECTURE}) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/json11.pc" DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig) diff --git a/community/c++-algo/include/json11/LICENSE.txt b/community/c++-algo/include/json11/LICENSE.txt new file mode 100644 index 00000000..691742e9 --- /dev/null +++ b/community/c++-algo/include/json11/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013 Dropbox, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/community/c++-algo/include/json11/README.md b/community/c++-algo/include/json11/README.md new file mode 100644 index 00000000..21f4e8b2 --- /dev/null +++ b/community/c++-algo/include/json11/README.md @@ -0,0 +1,42 @@ +json11 +------ + +json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. + +The core object provided by the library is json11::Json. A Json object represents any JSON +value: null, bool, number (int or double), string (std::string), array (std::vector), or +object (std::map). + +Json objects act like values. They can be assigned, copied, moved, compared for equality or +order, and so on. There are also helper methods Json::dump, to serialize a Json to a string, and +Json::parse (static) to parse a std::string as a Json object. + +It's easy to make a JSON object with C++11's new initializer syntax: + + Json my_json = Json::object { + { "key1", "value1" }, + { "key2", false }, + { "key3", Json::array { 1, 2, 3 } }, + }; + std::string json_str = my_json.dump(); + +There are also implicit constructors that allow standard and user-defined types to be +automatically converted to JSON. For example: + + class Point { + public: + int x; + int y; + Point (int x, int y) : x(x), y(y) {} + Json to_json() const { return Json::array { x, y }; } + }; + + std::vector points = { { 1, 2 }, { 10, 20 }, { 100, 200 } }; + std::string points_json = Json(points).dump(); + +JSON values can have their values queried and inspected: + + Json json = Json::array { Json::object { { "k", "v" } } }; + std::string str = json[0]["k"].string_value(); + +More documentation is still to come. For now, see json11.hpp. diff --git a/community/c++-algo/include/json11/json11.cpp b/community/c++-algo/include/json11/json11.cpp new file mode 100644 index 00000000..549463d7 --- /dev/null +++ b/community/c++-algo/include/json11/json11.cpp @@ -0,0 +1,788 @@ +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "json11.hpp" +#include +#include +#include +#include +#include + +namespace json11 { + +static const int max_depth = 200; + +using std::string; +using std::vector; +using std::map; +using std::make_shared; +using std::initializer_list; +using std::move; + +/* Helper for representing null - just a do-nothing struct, plus comparison + * operators so the helpers in JsonValue work. We can't use nullptr_t because + * it may not be orderable. + */ +struct NullStruct { + bool operator==(NullStruct) const { return true; } + bool operator<(NullStruct) const { return false; } +}; + +/* * * * * * * * * * * * * * * * * * * * + * Serialization + */ + +static void dump(NullStruct, string &out) { + out += "null"; +} + +static void dump(double value, string &out) { + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } +} + +static void dump(int value, string &out) { + char buf[32]; + snprintf(buf, sizeof buf, "%d", value); + out += buf; +} + +static void dump(bool value, string &out) { + out += value ? "true" : "false"; +} + +static void dump(const string &value, string &out) { + out += '"'; + for (size_t i = 0; i < value.length(); i++) { + const char ch = value[i]; + if (ch == '\\') { + out += "\\\\"; + } else if (ch == '"') { + out += "\\\""; + } else if (ch == '\b') { + out += "\\b"; + } else if (ch == '\f') { + out += "\\f"; + } else if (ch == '\n') { + out += "\\n"; + } else if (ch == '\r') { + out += "\\r"; + } else if (ch == '\t') { + out += "\\t"; + } else if (static_cast(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa9) { + out += "\\u2029"; + i += 2; + } else { + out += ch; + } + } + out += '"'; +} + +static void dump(const Json::array &values, string &out) { + bool first = true; + out += "["; + for (const auto &value : values) { + if (!first) + out += ", "; + value.dump(out); + first = false; + } + out += "]"; +} + +static void dump(const Json::object &values, string &out) { + bool first = true; + out += "{"; + for (const auto &kv : values) { + if (!first) + out += ", "; + dump(kv.first, out); + out += ": "; + kv.second.dump(out); + first = false; + } + out += "}"; +} + +void Json::dump(string &out) const { + m_ptr->dump(out); +} + +/* * * * * * * * * * * * * * * * * * * * + * Value wrappers + */ + +template +class Value : public JsonValue { +protected: + + // Constructors + explicit Value(const T &value) : m_value(value) {} + explicit Value(T &&value) : m_value(move(value)) {} + + // Get type tag + Json::Type type() const override { + return tag; + } + + // Comparisons + bool equals(const JsonValue * other) const override { + return m_value == static_cast *>(other)->m_value; + } + bool less(const JsonValue * other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + void dump(string &out) const override { json11::dump(m_value, out); } +}; + +class JsonDouble final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return static_cast(m_value); } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonDouble(double value) : Value(value) {} +}; + +class JsonInt final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return m_value; } + bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } + bool less(const JsonValue * other) const override { return m_value < other->number_value(); } +public: + explicit JsonInt(int value) : Value(value) {} +}; + +class JsonBoolean final : public Value { + bool bool_value() const override { return m_value; } +public: + explicit JsonBoolean(bool value) : Value(value) {} +}; + +class JsonString final : public Value { + const string &string_value() const override { return m_value; } +public: + explicit JsonString(const string &value) : Value(value) {} + explicit JsonString(string &&value) : Value(move(value)) {} +}; + +class JsonArray final : public Value { + const Json::array &array_items() const override { return m_value; } + const Json & operator[](size_t i) const override; +public: + explicit JsonArray(const Json::array &value) : Value(value) {} + explicit JsonArray(Json::array &&value) : Value(move(value)) {} +}; + +class JsonObject final : public Value { + const Json::object &object_items() const override { return m_value; } + const Json & operator[](const string &key) const override; +public: + explicit JsonObject(const Json::object &value) : Value(value) {} + explicit JsonObject(Json::object &&value) : Value(move(value)) {} +}; + +class JsonNull final : public Value { +public: + JsonNull() : Value({}) {} +}; + +/* * * * * * * * * * * * * * * * * * * * + * Static globals - static-init-safe + */ +struct Statics { + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map empty_map; + Statics() {} +}; + +static const Statics & statics() { + static const Statics s {}; + return s; +} + +static const Json & static_null() { + // This has to be separate, not in Statics, because Json() accesses statics().null. + static const Json json_null; + return json_null; +} + +/* * * * * * * * * * * * * * * * * * * * + * Constructors + */ + +Json::Json() noexcept : m_ptr(statics().null) {} +Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} +Json::Json(double value) : m_ptr(make_shared(value)) {} +Json::Json(int value) : m_ptr(make_shared(value)) {} +Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} +Json::Json(const string &value) : m_ptr(make_shared(value)) {} +Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} +Json::Json(const char * value) : m_ptr(make_shared(value)) {} +Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} + +/* * * * * * * * * * * * * * * * * * * * + * Accessors + */ + +Json::Type Json::type() const { return m_ptr->type(); } +double Json::number_value() const { return m_ptr->number_value(); } +int Json::int_value() const { return m_ptr->int_value(); } +bool Json::bool_value() const { return m_ptr->bool_value(); } +const string & Json::string_value() const { return m_ptr->string_value(); } +const vector & Json::array_items() const { return m_ptr->array_items(); } +const map & Json::object_items() const { return m_ptr->object_items(); } +const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } +const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } + +double JsonValue::number_value() const { return 0; } +int JsonValue::int_value() const { return 0; } +bool JsonValue::bool_value() const { return false; } +const string & JsonValue::string_value() const { return statics().empty_string; } +const vector & JsonValue::array_items() const { return statics().empty_vector; } +const map & JsonValue::object_items() const { return statics().empty_map; } +const Json & JsonValue::operator[] (size_t) const { return static_null(); } +const Json & JsonValue::operator[] (const string &) const { return static_null(); } + +const Json & JsonObject::operator[] (const string &key) const { + auto iter = m_value.find(key); + return (iter == m_value.end()) ? static_null() : iter->second; +} +const Json & JsonArray::operator[] (size_t i) const { + if (i >= m_value.size()) return static_null(); + else return m_value[i]; +} + +/* * * * * * * * * * * * * * * * * * * * + * Comparison + */ + +bool Json::operator== (const Json &other) const { + if (m_ptr == other.m_ptr) + return true; + if (m_ptr->type() != other.m_ptr->type()) + return false; + + return m_ptr->equals(other.m_ptr.get()); +} + +bool Json::operator< (const Json &other) const { + if (m_ptr == other.m_ptr) + return false; + if (m_ptr->type() != other.m_ptr->type()) + return m_ptr->type() < other.m_ptr->type(); + + return m_ptr->less(other.m_ptr.get()); +} + +/* * * * * * * * * * * * * * * * * * * * + * Parsing + */ + +/* esc(c) + * + * Format char c suitable for printing in an error message. + */ +static inline string esc(char c) { + char buf[12]; + if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { + snprintf(buf, sizeof buf, "'%c' (%d)", c, c); + } else { + snprintf(buf, sizeof buf, "(%d)", c); + } + return string(buf); +} + +static inline bool in_range(long x, long lower, long upper) { + return (x >= lower && x <= upper); +} + +namespace { +/* JsonParser + * + * Object that tracks all state of an in-progress parse. + */ +struct JsonParser final { + + /* State + */ + const string &str; + size_t i; + string &err; + bool failed; + const JsonParse strategy; + + /* fail(msg, err_ret = Json()) + * + * Mark this parse as failed. + */ + Json fail(string &&msg) { + return fail(move(msg), Json()); + } + + template + T fail(string &&msg, const T err_ret) { + if (!failed) + err = std::move(msg); + failed = true; + return err_ret; + } + + /* consume_whitespace() + * + * Advance until the current character is non-whitespace. + */ + void consume_whitespace() { + while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') + i++; + } + + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input after start of comment", false); + if (str[i] == '/') { // inline comment + i++; + // advance until next line, or end of input + while (i < str.size() && str[i] != '\n') { + i++; + } + comment_found = true; + } + else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size()-2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i+1] == '/')) { + i++; + if (i > str.size()-2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + comment_found = true; + } + else + return fail("malformed comment", false); + } + return comment_found; + } + + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if(strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + if (failed) return; + consume_whitespace(); + } + while(comment_found); + } + } + + /* get_next_token() + * + * Return the next non-whitespace character. If the end of the input is reached, + * flag an error and return 0. + */ + char get_next_token() { + consume_garbage(); + if (failed) return static_cast(0); + if (i == str.size()) + return fail("unexpected end of input", static_cast(0)); + + return str[i++]; + } + + /* encode_utf8(pt, out) + * + * Encode pt as UTF-8 and add it to out. + */ + void encode_utf8(long pt, string & out) { + if (pt < 0) + return; + + if (pt < 0x80) { + out += static_cast(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } + } + + /* parse_string() + * + * Parse a string, starting at the current position. + */ + string parse_string() { + string out; + long last_escaped_codepoint = -1; + while (true) { + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + char ch = str[i++]; + + if (ch == '"') { + encode_utf8(last_escaped_codepoint, out); + return out; + } + + if (in_range(ch, 0, 0x1f)) + return fail("unescaped " + esc(ch) + " in string", ""); + + // The usual case: non-escaped characters + if (ch != '\\') { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + out += ch; + continue; + } + + // Handle escapes + if (i == str.size()) + return fail("unexpected end of input in string", ""); + + ch = str[i++]; + + if (ch == 'u') { + // Extract 4-byte escape sequence + string esc = str.substr(i, 4); + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { + if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') + && !in_range(esc[j], '0', '9')) + return fail("bad \\u escape: " + esc, ""); + } + + long codepoint = strtol(esc.data(), nullptr, 16); + + // JSON specifies that characters outside the BMP shall be encoded as a pair + // of 4-hex-digit \u escapes encoding their surrogate pair components. Check + // whether we're in the middle of such a beast: the previous codepoint was an + // escaped lead (high) surrogate, and this is a trail (low) surrogate. + if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) + && in_range(codepoint, 0xDC00, 0xDFFF)) { + // Reassemble the two surrogate pairs into one astral-plane character, per + // the UTF-16 algorithm. + encode_utf8((((last_escaped_codepoint - 0xD800) << 10) + | (codepoint - 0xDC00)) + 0x10000, out); + last_escaped_codepoint = -1; + } else { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = codepoint; + } + + i += 4; + continue; + } + + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + + if (ch == 'b') { + out += '\b'; + } else if (ch == 'f') { + out += '\f'; + } else if (ch == 'n') { + out += '\n'; + } else if (ch == 'r') { + out += '\r'; + } else if (ch == 't') { + out += '\t'; + } else if (ch == '"' || ch == '\\' || ch == '/') { + out += ch; + } else { + return fail("invalid escape character " + esc(ch), ""); + } + } + } + + /* parse_number() + * + * Parse a double. + */ + Json parse_number() { + size_t start_pos = i; + + if (str[i] == '-') + i++; + + // Integer part + if (str[i] == '0') { + i++; + if (in_range(str[i], '0', '9')) + return fail("leading 0s not permitted in numbers"); + } else if (in_range(str[i], '1', '9')) { + i++; + while (in_range(str[i], '0', '9')) + i++; + } else { + return fail("invalid " + esc(str[i]) + " in number"); + } + + if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' + && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { + return std::atoi(str.c_str() + start_pos); + } + + // Decimal part + if (str[i] == '.') { + i++; + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in fractional part"); + + while (in_range(str[i], '0', '9')) + i++; + } + + // Exponent part + if (str[i] == 'e' || str[i] == 'E') { + i++; + + if (str[i] == '+' || str[i] == '-') + i++; + + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in exponent"); + + while (in_range(str[i], '0', '9')) + i++; + } + + return std::strtod(str.c_str() + start_pos, nullptr); + } + + /* expect(str, res) + * + * Expect that 'str' starts at the character that was just read. If it does, advance + * the input and return res. If not, flag an error. + */ + Json expect(const string &expected, Json res) { + assert(i != 0); + i--; + if (str.compare(i, expected.length(), expected) == 0) { + i += expected.length(); + return res; + } else { + return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); + } + } + + /* parse_json() + * + * Parse a JSON object. + */ + Json parse_json(int depth) { + if (depth > max_depth) { + return fail("exceeded maximum nesting depth"); + } + + char ch = get_next_token(); + if (failed) + return Json(); + + if (ch == '-' || (ch >= '0' && ch <= '9')) { + i--; + return parse_number(); + } + + if (ch == 't') + return expect("true", true); + + if (ch == 'f') + return expect("false", false); + + if (ch == 'n') + return expect("null", Json()); + + if (ch == '"') + return parse_string(); + + if (ch == '{') { + map data; + ch = get_next_token(); + if (ch == '}') + return data; + + while (1) { + if (ch != '"') + return fail("expected '\"' in object, got " + esc(ch)); + + string key = parse_string(); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch != ':') + return fail("expected ':' in object, got " + esc(ch)); + + data[std::move(key)] = parse_json(depth + 1); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == '}') + break; + if (ch != ',') + return fail("expected ',' in object, got " + esc(ch)); + + ch = get_next_token(); + } + return data; + } + + if (ch == '[') { + vector data; + ch = get_next_token(); + if (ch == ']') + return data; + + while (1) { + i--; + data.push_back(parse_json(depth + 1)); + if (failed) + return Json(); + + ch = get_next_token(); + if (ch == ']') + break; + if (ch != ',') + return fail("expected ',' in list, got " + esc(ch)); + + ch = get_next_token(); + (void)ch; + } + return data; + } + + return fail("expected value, got " + esc(ch)); + } +}; +}//namespace { + +Json Json::parse(const string &in, string &err, JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + Json result = parser.parse_json(0); + + // Check for any trailing garbage + parser.consume_garbage(); + if (parser.failed) + return Json(); + if (parser.i != in.size()) + return parser.fail("unexpected trailing " + esc(in[parser.i])); + + return result; +} + +// Documented in json11.hpp +vector Json::parse_multi(const string &in, + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser { in, 0, err, false, strategy }; + parser_stop_pos = 0; + vector json_vec; + while (parser.i != in.size() && !parser.failed) { + json_vec.push_back(parser.parse_json(0)); + if (parser.failed) + break; + + // Check for another object + parser.consume_garbage(); + if (parser.failed) + break; + parser_stop_pos = parser.i; + } + return json_vec; +} + +/* * * * * * * * * * * * * * * * * * * * + * Shape-checking + */ + +bool Json::has_shape(const shape & types, string & err) const { + if (!is_object()) { + err = "expected JSON object, got " + dump(); + return false; + } + + for (auto & item : types) { + if ((*this)[item.first].type() != item.second) { + err = "bad type for " + item.first + " in " + dump(); + return false; + } + } + + return true; +} + +} // namespace json11 diff --git a/community/c++-algo/include/json11/json11.hpp b/community/c++-algo/include/json11/json11.hpp new file mode 100644 index 00000000..0c47d050 --- /dev/null +++ b/community/c++-algo/include/json11/json11.hpp @@ -0,0 +1,232 @@ +/* json11 + * + * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. + * + * The core object provided by the library is json11::Json. A Json object represents any JSON + * value: null, bool, number (int or double), string (std::string), array (std::vector), or + * object (std::map). + * + * Json objects act like values: they can be assigned, copied, moved, compared for equality or + * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and + * Json::parse (static) to parse a std::string as a Json object. + * + * Internally, the various types of Json object are represented by the JsonValue class + * hierarchy. + * + * A note on numbers - JSON specifies the syntax of number formatting but not its semantics, + * so some JSON implementations distinguish between integers and floating-point numbers, while + * some don't. In json11, we choose the latter. Because some JSON implementations (namely + * Javascript itself) treat all numbers as the same type, distinguishing the two leads + * to JSON that will be *silently* changed by a round-trip through those implementations. + * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also + * provides integer helpers. + * + * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the + * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64 + * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch + * will be exact for +/- 275 years.) + */ + +/* Copyright (c) 2013 Dropbox, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept + #define noexcept throw() + #endif + + #ifndef snprintf + #define snprintf _snprintf_s + #endif + #endif +#endif + +namespace json11 { + +enum JsonParse { + STANDARD, COMMENTS +}; + +class JsonValue; + +class Json final { +public: + // Types + enum Type { + NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT + }; + + // Array and object typedefs + typedef std::vector array; + typedef std::map object; + + // Constructors for the various types of JSON value. + Json() noexcept; // NUL + Json(std::nullptr_t) noexcept; // NUL + Json(double value); // NUMBER + Json(int value); // NUMBER + Json(bool value); // BOOL + Json(const std::string &value); // STRING + Json(std::string &&value); // STRING + Json(const char * value); // STRING + Json(const array &values); // ARRAY + Json(array &&values); // ARRAY + Json(const object &values); // OBJECT + Json(object &&values); // OBJECT + + // Implicit constructor: anything with a to_json() function. + template + Json(const T & t) : Json(t.to_json()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template ().begin()->first)>::value + && std::is_constructible().begin()->second)>::value, + int>::type = 0> + Json(const M & m) : Json(object(m.begin(), m.end())) {} + + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template ().begin())>::value, + int>::type = 0> + Json(const V & v) : Json(array(v.begin(), v.end())) {} + + // This prevents Json(some_pointer) from accidentally producing a bool. Use + // Json(bool(some_pointer)) if that behavior is desired. + Json(void *) = delete; + + // Accessors + Type type() const; + + bool is_null() const { return type() == NUL; } + bool is_number() const { return type() == NUMBER; } + bool is_bool() const { return type() == BOOL; } + bool is_string() const { return type() == STRING; } + bool is_array() const { return type() == ARRAY; } + bool is_object() const { return type() == OBJECT; } + + // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not + // distinguish between integer and non-integer numbers - number_value() and int_value() + // can both be applied to a NUMBER-typed object. + double number_value() const; + int int_value() const; + + // Return the enclosed value if this is a boolean, false otherwise. + bool bool_value() const; + // Return the enclosed string if this is a string, "" otherwise. + const std::string &string_value() const; + // Return the enclosed std::vector if this is an array, or an empty vector otherwise. + const array &array_items() const; + // Return the enclosed std::map if this is an object, or an empty map otherwise. + const object &object_items() const; + + // Return a reference to arr[i] if this is an array, Json() otherwise. + const Json & operator[](size_t i) const; + // Return a reference to obj[key] if this is an object, Json() otherwise. + const Json & operator[](const std::string &key) const; + + // Serialize. + void dump(std::string &out) const; + std::string dump() const { + std::string out; + dump(out); + return out; + } + + // Parse. If parse fails, return Json() and assign an error message to err. + static Json parse(const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + static Json parse(const char * in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + if (in) { + return parse(std::string(in), err, strategy); + } else { + err = "null input"; + return nullptr; + } + } + // Parse multiple objects, concatenated or separated by whitespace + static std::vector parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string & in, + std::string & err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } + + bool operator== (const Json &rhs) const; + bool operator< (const Json &rhs) const; + bool operator!= (const Json &rhs) const { return !(*this == rhs); } + bool operator<= (const Json &rhs) const { return !(rhs < *this); } + bool operator> (const Json &rhs) const { return (rhs < *this); } + bool operator>= (const Json &rhs) const { return !(*this < rhs); } + + /* has_shape(types, err) + * + * Return true if this is a JSON object and, for each item in types, has a field of + * the given type. If not, return false and set err to a descriptive message. + */ + typedef std::initializer_list> shape; + bool has_shape(const shape & types, std::string & err) const; + +private: + std::shared_ptr m_ptr; +}; + +// Internal class hierarchy - JsonValue objects are not exposed to users of this API. +class JsonValue { +protected: + friend class Json; + friend class JsonInt; + friend class JsonDouble; + virtual Json::Type type() const = 0; + virtual bool equals(const JsonValue * other) const = 0; + virtual bool less(const JsonValue * other) const = 0; + virtual void dump(std::string &out) const = 0; + virtual double number_value() const; + virtual int int_value() const; + virtual bool bool_value() const; + virtual const std::string &string_value() const; + virtual const Json::array &array_items() const; + virtual const Json &operator[](size_t i) const; + virtual const Json::object &object_items() const; + virtual const Json &operator[](const std::string &key) const; + virtual ~JsonValue() {} +}; + +} // namespace json11 diff --git a/community/c++-algo/include/json11/json11.pc.in b/community/c++-algo/include/json11/json11.pc.in new file mode 100644 index 00000000..63fb2414 --- /dev/null +++ b/community/c++-algo/include/json11/json11.pc.in @@ -0,0 +1,9 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${prefix}/lib/@CMAKE_LIBRARY_ARCHITECTURE@ +includedir=${prefix}/include/@CMAKE_LIBRARY_ARCHITECTURE@ + +Name: @PROJECT_NAME@ +Description: json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -ljson11 +Cflags: -I${includedir} diff --git a/community/c++-algo/include/json11/test.cpp b/community/c++-algo/include/json11/test.cpp new file mode 100644 index 00000000..ab2e2b95 --- /dev/null +++ b/community/c++-algo/include/json11/test.cpp @@ -0,0 +1,281 @@ +/* + * Define JSON11_TEST_CUSTOM_CONFIG to 1 if you want to build this tester into + * your own unit-test framework rather than a stand-alone program. By setting + * The values of the variables included below, you can insert your own custom + * code into this file as it builds, in order to make it into a test case for + * your favorite framework. + */ +#if !JSON11_TEST_CUSTOM_CONFIG +#define JSON11_TEST_CPP_PREFIX_CODE +#define JSON11_TEST_CPP_SUFFIX_CODE +#define JSON11_TEST_STANDALONE_MAIN 1 +#define JSON11_TEST_CASE(name) static void name() +#define JSON11_TEST_ASSERT(b) assert(b) +#ifdef NDEBUG +#undef NDEBUG//at now assert will work even in Release build +#endif +#endif // JSON11_TEST_CUSTOM_CONFIG + +/* + * Enable or disable code which demonstrates the behavior change in Xcode 7 / Clang 3.7, + * introduced by DR1467 and described here: https://github.com/dropbox/json11/issues/86 + * Defaults to off since it doesn't appear the standards committee is likely to act + * on this, so it needs to be considered normal behavior. + */ +#ifndef JSON11_ENABLE_DR1467_CANARY +#define JSON11_ENABLE_DR1467_CANARY 0 +#endif + +/* + * Beginning of standard source file, which makes use of the customizations above. + */ +#include +#include +#include +#include +#include +#include +#include "json11.hpp" +#include +#include +#include +#include +#include + +// Insert user-defined prefix code (includes, function declarations, etc) +// to set up a custom test suite +JSON11_TEST_CPP_PREFIX_CODE + +using namespace json11; +using std::string; + +// Check that Json has the properties we want. +#define CHECK_TRAIT(x) static_assert(std::x::value, #x) +CHECK_TRAIT(is_nothrow_constructible); +CHECK_TRAIT(is_nothrow_default_constructible); +CHECK_TRAIT(is_copy_constructible); +CHECK_TRAIT(is_nothrow_move_constructible); +CHECK_TRAIT(is_copy_assignable); +CHECK_TRAIT(is_nothrow_move_assignable); +CHECK_TRAIT(is_nothrow_destructible); + +JSON11_TEST_CASE(json11_test) { + const string simple_test = + R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})"; + + string err; + const auto json = Json::parse(simple_test, err); + + std::cout << "k1: " << json["k1"].string_value() << "\n"; + std::cout << "k3: " << json["k3"].dump() << "\n"; + + for (auto &k : json["k3"].array_items()) { + std::cout << " - " << k.dump() << "\n"; + } + + string comment_test = R"({ + // comment /* with nested comment */ + "a": 1, + // comment + // continued + "b": "text", + /* multi + line + comment + // line-comment-inside-multiline-comment + */ + // and single-line comment + // and single-line comment /* multiline inside single line */ + "c": [1, 2, 3] + // and single-line comment at end of object + })"; + + string err_comment; + auto json_comment = Json::parse( + comment_test, err_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(!json_comment.is_null()); + JSON11_TEST_ASSERT(err_comment.empty()); + + comment_test = "{\"a\": 1}//trailing line comment"; + json_comment = Json::parse( + comment_test, err_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(!json_comment.is_null()); + JSON11_TEST_ASSERT(err_comment.empty()); + + comment_test = "{\"a\": 1}/*trailing multi-line comment*/"; + json_comment = Json::parse( + comment_test, err_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(!json_comment.is_null()); + JSON11_TEST_ASSERT(err_comment.empty()); + + string failing_comment_test = "{\n/* unterminated comment\n\"a\": 1,\n}"; + string err_failing_comment; + auto json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + failing_comment_test = "{\n/* unterminated trailing comment }"; + json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + failing_comment_test = "{\n/ / bad comment }"; + json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + failing_comment_test = "{// bad comment }"; + json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + failing_comment_test = "{\n\"a\": 1\n}/"; + json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + failing_comment_test = "{/* bad\ncomment *}"; + json_failing_comment = Json::parse( + failing_comment_test, err_failing_comment, JsonParse::COMMENTS); + JSON11_TEST_ASSERT(json_failing_comment.is_null()); + JSON11_TEST_ASSERT(!err_failing_comment.empty()); + + std::list l1 { 1, 2, 3 }; + std::vector l2 { 1, 2, 3 }; + std::set l3 { 1, 2, 3 }; + JSON11_TEST_ASSERT(Json(l1) == Json(l2)); + JSON11_TEST_ASSERT(Json(l2) == Json(l3)); + + std::map m1 { { "k1", "v1" }, { "k2", "v2" } }; + std::unordered_map m2 { { "k1", "v1" }, { "k2", "v2" } }; + JSON11_TEST_ASSERT(Json(m1) == Json(m2)); + + // Json literals + const Json obj = Json::object({ + { "k1", "v1" }, + { "k2", 42.0 }, + { "k3", Json::array({ "a", 123.0, true, false, nullptr }) }, + }); + + std::cout << "obj: " << obj.dump() << "\n"; + JSON11_TEST_ASSERT(obj.dump() == "{\"k1\": \"v1\", \"k2\": 42, \"k3\": [\"a\", 123, true, false, null]}"); + + JSON11_TEST_ASSERT(Json("a").number_value() == 0); + JSON11_TEST_ASSERT(Json("a").string_value() == "a"); + JSON11_TEST_ASSERT(Json().number_value() == 0); + + JSON11_TEST_ASSERT(obj == json); + JSON11_TEST_ASSERT(Json(42) == Json(42.0)); + JSON11_TEST_ASSERT(Json(42) != Json(42.1)); + + const string unicode_escape_test = + R"([ "blah\ud83d\udca9blah\ud83dblah\udca9blah\u0000blah\u1234" ])"; + + const char utf8[] = "blah" "\xf0\x9f\x92\xa9" "blah" "\xed\xa0\xbd" "blah" + "\xed\xb2\xa9" "blah" "\0" "blah" "\xe1\x88\xb4"; + + Json uni = Json::parse(unicode_escape_test, err); + JSON11_TEST_ASSERT(uni[0].string_value().size() == (sizeof utf8) - 1); + JSON11_TEST_ASSERT(std::memcmp(uni[0].string_value().data(), utf8, sizeof utf8) == 0); + + // Demonstrates the behavior change in Xcode 7 / Clang 3.7, introduced by DR1467 + // and described here: https://llvm.org/bugs/show_bug.cgi?id=23812 + if (JSON11_ENABLE_DR1467_CANARY) { + Json nested_array = Json::array { Json::array { 1, 2, 3 } }; + JSON11_TEST_ASSERT(nested_array.is_array()); + JSON11_TEST_ASSERT(nested_array.array_items().size() == 1); + JSON11_TEST_ASSERT(nested_array.array_items()[0].is_array()); + JSON11_TEST_ASSERT(nested_array.array_items()[0].array_items().size() == 3); + } + + { + const std::string good_json = R"( {"k1" : "v1"})"; + const std::string bad_json1 = good_json + " {"; + const std::string bad_json2 = good_json + R"({"k2":"v2", "k3":[)"; + struct TestMultiParse { + std::string input; + std::string::size_type expect_parser_stop_pos; + size_t expect_not_empty_elms_count; + Json expect_parse_res; + } tests[] = { + {" {", 0, 0, {}}, + {good_json, good_json.size(), 1, Json(std::map{ { "k1", "v1" } })}, + {bad_json1, good_json.size() + 1, 1, Json(std::map{ { "k1", "v1" } })}, + {bad_json2, good_json.size(), 1, Json(std::map{ { "k1", "v1" } })}, + {"{}", 2, 1, Json::object{}}, + }; + for (const auto &tst : tests) { + std::string::size_type parser_stop_pos; + std::string err; + auto res = Json::parse_multi(tst.input, parser_stop_pos, err); + JSON11_TEST_ASSERT(parser_stop_pos == tst.expect_parser_stop_pos); + JSON11_TEST_ASSERT( + (size_t)std::count_if(res.begin(), res.end(), + [](const Json& j) { return !j.is_null(); }) + == tst.expect_not_empty_elms_count); + if (!res.empty()) { + JSON11_TEST_ASSERT(tst.expect_parse_res == res[0]); + } + } + } + + Json my_json = Json::object { + { "key1", "value1" }, + { "key2", false }, + { "key3", Json::array { 1, 2, 3 } }, + }; + std::string json_obj_str = my_json.dump(); + std::cout << "json_obj_str: " << json_obj_str << "\n"; + JSON11_TEST_ASSERT(json_obj_str == "{\"key1\": \"value1\", \"key2\": false, \"key3\": [1, 2, 3]}"); + + class Point { + public: + int x; + int y; + Point (int x, int y) : x(x), y(y) {} + Json to_json() const { return Json::array { x, y }; } + }; + + std::vector points = { { 1, 2 }, { 10, 20 }, { 100, 200 } }; + std::string points_json = Json(points).dump(); + std::cout << "points_json: " << points_json << "\n"; + JSON11_TEST_ASSERT(points_json == "[[1, 2], [10, 20], [100, 200]]"); +} + +#if JSON11_TEST_STANDALONE_MAIN + +static void parse_from_stdin() { + string buf; + string line; + while (std::getline(std::cin, line)) { + buf += line + "\n"; + } + + string err; + auto json = Json::parse(buf, err); + if (!err.empty()) { + printf("Failed: %s\n", err.c_str()); + } else { + printf("Result: %s\n", json.dump().c_str()); + } +} + +int main(int argc, char **argv) { + if (argc == 2 && argv[1] == string("--stdin")) { + parse_from_stdin(); + return 0; + } + + json11_test(); +} + +#endif // JSON11_TEST_STANDALONE_MAIN + +// Insert user-defined suffix code (function definitions, etc) +// to set up a custom test suite +JSON11_TEST_CPP_SUFFIX_CODE diff --git a/community/c++-algo/src/algoStrategy.cpp b/community/c++-algo/src/algoStrategy.cpp new file mode 100644 index 00000000..384f1127 --- /dev/null +++ b/community/c++-algo/src/algoStrategy.cpp @@ -0,0 +1,225 @@ +/* +Description: Implementations for the algoStrategy header. +Last Modified: 09 Apr 2019 +Author: Isaac Draper +*/ + +#include "algoStrategy.h" + +namespace terminal { + + using json11::Json; + using std::string; + using std::vector; + + /* + Most of the algo code you write will be in this file unless you create new + modules yourself. Start by modifying the 'on_turn' function. + + Advanced strategy tips : + Additional functions are made available by importing the AdvancedGameState + class from gamelib / advanced.py as a replacement for the regular GameState class in game.py. + + You can analyze action frames by modifying algoCore.h/cpp. + + The GameState.map object can be manually manipulated to create hypothetical + board states.Though, it is recommended to make a copy of the map to preserve + the actual current map state. + */ + + /// Basic constructor for algoCore which calls it's superclass, AlgoCore. + AlgoStrategy::AlgoStrategy() : AlgoCore::AlgoCore() { + + } + + /// A function that runs at the start of every game. + /// This is overridden from AlgoCore to perform initial setup + /// at the start of the game, based on the configuration. + /// @param configuration A Json object containing information about the game. + void AlgoStrategy::onGameStart(Json configuration) { + Util::debugWrite("Configuring your custom algo strategy..."); + config = configuration; + } + + /// Called when the engine expects input from the algo. + /// It is called every turn and is passed a Json object containing + /// the current game state, which can be used to initialize a new gameState. + /// This is overridden from AlgoCore. + /// @param jsonState A Json object containing the current game state. + void AlgoStrategy::onTurn(Json jsonState) { + + // Create a gamestate object that will help execute our strategy. + GameState gameState = GameState(config, jsonState); + + // Print so we know we started our strategy. + Util::debugWrite("Performing turn ", false); + Util::debugWrite(gameState.getTurn(), false); + Util::debugWrite(" of your C++ starter strategy."); + + // gameState.suppressWarnings(); // Uncomment this to suppress warnings. + + // Run our strategy. + starterStrategy(gameState); + + // Submit our turn to the engine. + gameState.submitTurn(); + } + + /* + NOTE: All the methods after this point are part of the sample starter - algo + strategy and can safely be replaced for your custom algo. + */ + + /// This starts using the gameState to run our strategy. + /// @param gameState The current game state of the board (by reference). + void AlgoStrategy::starterStrategy(GameState& gameState) { + + // Builds the C1 Logo. + // Calling this first prioritizes repairing the logo before all else. + buildC1Logo(gameState); + + // Then build additional defences. + buildDefences(gameState); + + // Finally deploy our information units to attack. + deployAttackers(gameState); + } + + /// Here we make the C1 logo. + /// We use Filter firewalls because they are cheap. + /// @param gameState The current game state of the board (by reference). + void AlgoStrategy::buildC1Logo(GameState& gameState) { + + // Build the letter C. + vector letterC = { Pos {8, 11}, Pos {9, 11}, Pos {7, 10}, Pos {7, 9}, Pos {7, 8}, Pos {8, 7}, Pos {9, 7} }; + for (Pos pos : letterC) + if (gameState.canSpawn(FILTER, pos)) + gameState.attemptSpawn(FILTER, pos); + + // Build the number 1. + vector number1 = { Pos {17, 11}, Pos {18, 11}, Pos {18, 10}, Pos {18, 9}, Pos {18, 8}, Pos {17, 7}, Pos {18, 7}, Pos {19, 7} }; + for (Pos pos : number1) + if (gameState.canSpawn(FILTER, pos)) + gameState.attemptSpawn(FILTER, pos); + + // Build the 3 dots with destructors. + vector dots = { Pos {11, 7}, Pos {13, 9}, Pos {15, 11} }; + for (Pos pos : dots) + if (gameState.canSpawn(DESTRUCTOR, pos)) + gameState.attemptSpawn(DESTRUCTOR, pos); + } + + /// Builds some extra defences. + /// @param gameState The current game state of the board (by reference). + void AlgoStrategy::buildDefences(GameState& gameState) { + + // First we protect ourselves with some destructors. + vector destructorLocations = { Pos {0, 13}, Pos {27, 13} }; + for (Pos pos : destructorLocations) + if (gameState.canSpawn(DESTRUCTOR, pos)) + gameState.attemptSpawn(DESTRUCTOR, pos); + + // Then lets boost our offense by building some encryptors to shield + // our information units.Lets put them near the front because the + // shields decay over time, so shields closer to the action + // are more effective. + vector encryptorLocations = { Pos {3, 11}, Pos {4, 11}, Pos {5, 11} }; + for (Pos pos : encryptorLocations) + if (gameState.canSpawn(ENCRYPTOR, pos)) + gameState.attemptSpawn(ENCRYPTOR, pos); + + // Lastly lets build encryptors in random locations.Normally building + // randomly is a bad idea but we'll leave it to you to figure out better strategies. + // + // First we get all locations on the bottom half of the map + // that are in the arena bounds. + vector allLocations; + for (unsigned int i = 0; i < gameState.gameMap.ARENA_SIZE; ++i) { + for (unsigned int j = 0; j < gameState.gameMap.HALF_ARENA; ++j) { + if (gameState.gameMap.inArenaBounds(i, j)) { + allLocations.push_back(Pos{ i, j }); + } + } + } + + // Then we remove loactions already occupied. + vector possibleLocations = filterBlockedLocations(allLocations, gameState); + + // While we have cores to spend, build random Encryptors. + while (gameState.getResource(CORES) >= gameState.typeCost(ENCRYPTOR) && !possibleLocations.empty()) { + // Choose a random location. + int randomIndex = rand() % possibleLocations.size(); + Pos buildLocation = possibleLocations.at(randomIndex); + + + // Build at that location and remove it from our vector. + gameState.attemptSpawn(ENCRYPTOR, buildLocation); + possibleLocations.erase(possibleLocations.begin() + randomIndex); + } + } + + /// Deploys attackers to the edges of the map. + /// @param gameState The current game state of the board (by reference). + void AlgoStrategy::deployAttackers(GameState& gameState) { + + // First lets check if we have 10 bits, if we don't lets + // wait for a turn that we do. + if (gameState.getResource(BITS) < 10) + return; + + // Lets deploy an EMP long range unit to destry firewalls. + if (gameState.canSpawn(EMP, 3, 10)) + gameState.attemptSpawn(EMP, 3, 10); + + // Now lets send out 3 pings to try and score. + // We can spawn multiple at the same locatoin. + if (gameState.canSpawn(PING, 3, 10, 3)) + gameState.attemptSpawn(PING, 3, 10, 3); + + /* + NOTE: the locations we used above to spawn information units may become + blocked by our own firewalls. We'll leave it to you to fix that issue yourselves. + + Lastly, lets send out Scramblers to help destroy enemy information units. + A complex algo would predict where the enemy is going to send units and + develop its strategy around that. But this algo is simple so lets just + send out scramblers in random locations and hope for the best. + */ + + // Firstly, information units can only deploy on our edges. + // So lets get a list of those locations. + vector friendlyEdges; + gameState.gameMap.getEdgeLocations(friendlyEdges, BOTTOM_LEFT); + gameState.gameMap.getEdgeLocations(friendlyEdges, BOTTOM_RIGHT); + + // Remove locations blocked by our own firewalls. + vector deployLocations = filterBlockedLocations(friendlyEdges, gameState); + + // While we have bits to spend, build encryptors randomly. + while (gameState.getResource(BITS) >= gameState.typeCost(SCRAMBLER) && !deployLocations.empty()) { + // Choose a random location. + int randomIndex = rand() % deployLocations.size(); + Pos buildLocation = deployLocations.at(randomIndex); + + // Build at that location. + gameState.attemptSpawn(SCRAMBLER, buildLocation); + + // We don't have to remove the location since + // units can occupy the same space. + } + } + + /// This returns a vector of positions not already occupied. + /// @param locations A vector of locations to check. + /// @return A new vector with no positions that are blocked. + vector AlgoStrategy::filterBlockedLocations(vector& locations, GameState& gameState) const { + vector filtered; + for (Pos pos : locations) { + if (!gameState.gameMap.containsStationaryUnit(pos)) { + filtered.push_back(pos); + } + } + return filtered; + } + +} diff --git a/community/c++-algo/src/algoStrategy.h b/community/c++-algo/src/algoStrategy.h new file mode 100644 index 00000000..1d1d553b --- /dev/null +++ b/community/c++-algo/src/algoStrategy.h @@ -0,0 +1,49 @@ +/* +Description: A header to contain the components and logic of one's strategy. +Last Modified: 09 Apr 2019 +Author: Isaac Draper +*/ + +#ifndef ALGO_STRATEGY_H +#define ALGO_STRATEGY_H + +#include +#include + +#include "json11/json11.hpp" +#include "GameLib/src/gameState.h" +#include "GameLib/src/algoCore.h" +#include "GameLib/src/structs.h" +#include "GameLib/src/enums.h" +#include "GameLib/src/util.h" + +namespace terminal { + + using json11::Json; + + /// This class will contain most of the logic and be what you edit the most. + /// You should start this class and override the functions defined in + /// AlgoCore in order to define your own strategy. + class AlgoStrategy : public terminal::AlgoCore { + public: + AlgoStrategy(); + + private: + // Functions + // These two should always be part of your algo. + void onGameStart(Json configuration) override; + void onTurn(Json jsonState) override; + + // These are functions that can be modified/removed. + // They are used to build the C1 logo and send attacks. + void starterStrategy(GameState& gameState); + void buildC1Logo(GameState& gameState); + void buildDefences(GameState& gameState); + void deployAttackers(GameState& gameState); + vector filterBlockedLocations(vector& locations, GameState& gameState) const; + + }; + +} + +#endif diff --git a/community/c++-algo/src/source.cpp b/community/c++-algo/src/source.cpp new file mode 100644 index 00000000..5247f46c --- /dev/null +++ b/community/c++-algo/src/source.cpp @@ -0,0 +1,18 @@ +/* +Description: This is the main entry point for your algo. +Last Modified: 06 Apr 2019 +Author: Isaac Draper +*/ + +#include +#include + +#include "algoStrategy.h" + +int main(int argc, char * argv[]) { + + terminal::AlgoStrategy algoStrategy = terminal::AlgoStrategy(); + algoStrategy.start(); + + return 0; +}