Implemented Coding Contracts

This commit is contained in:
danielyxie 2018-09-22 19:25:48 -05:00
parent e714f1e6cd
commit f78f0ec1a7
17 changed files with 413 additions and 80 deletions

@ -0,0 +1,47 @@
.. _codingcontracts:
Coding Contracts
================
Coding Contracts are a mechanic that lets players earn rewards in
exchange for solving programming problems.
Coding Contracts are files with the ".cct" extensions. They can
be accessed through the :ref:`terminal` or through scripts using
the :ref:`netscriptcodingcontractapi`
Each contract has a limited number of attempts. If you
provide the wrong answer too many times and exceed the
number of attempts, the contract will self destruct (delete itself)
Currently, Coding Contracts are randomly generated and
spawned over time. They can appear on any server (including your
home computer), except for your purchased servers.
Running in Terminal
^^^^^^^^^^^^^^^^^^^
To run a Coding Contract in the Terminal, simply use the
:ref:`run_terminal_command` command::
$ run some-contract.cct
Doing this will bring up a popup. The popup will display
the contract's problem, the number of attempts remaining, and
an area to provide an answer.
Interacting through Scripts
^^^^^^^^^^^^^^^^^^^^^^^^^^^
See :ref:`netscriptcodingcontractapi`.
Rewards
^^^^^^^
There are currently four possible rewards for solving a Coding Contract:
* Faction Reputation for a specific Faction
* Faction Reputation for all Factions that you are a member of
* Company reputation for a specific Company
* Money
The 'amount' of reward varies based on the difficulty of the problem
posed by the Coding Contract. There is no way to know what a
Coding Contract's exact reward will be until it is solved.

@ -21,6 +21,7 @@ secrets that you've been searching for.
Netscript <netscript> Netscript <netscript>
Terminal <terminal> Terminal <terminal>
Coding Contracts <codingcontracts>
Keyboard Shortcuts <shortcuts> Keyboard Shortcuts <shortcuts>
Changelog <changelog> Changelog <changelog>

@ -25,4 +25,5 @@ to reach out to the developer!
Trade Information eXchange (TIX) API <netscriptixapi> Trade Information eXchange (TIX) API <netscriptixapi>
Singularity Functions <netscriptsingularityfunctions> Singularity Functions <netscriptsingularityfunctions>
Bladeburner API <netscriptbladeburnerapi> Bladeburner API <netscriptbladeburnerapi>
Coding Contract API <netscriptcodingcontractapi>
Miscellaneous <netscriptmisc> Miscellaneous <netscriptmisc>

@ -1,9 +1,8 @@
Netscript Bladeburner API Netscript Bladeburner API
========================= =========================
Netscript provides the following API for interacting with the game's Bladeburner mechanic. Netscript provides the following API for interacting with the game's Bladeburner mechanic.
The Bladeburner API is **not** immediately available to the palyer and must be unlocked The Bladeburner API is **not** immediately available to the player and must be unlocked
later in the game later in the game
**WARNING: This page contains spoilers for the game** **WARNING: This page contains spoilers for the game**
@ -12,9 +11,9 @@ The Bladeburner API is unlocked in BitNode-7. If you are in BitNode-7, you will
automatically gain access to this API. Otherwise, you must have Source-File 7 in automatically gain access to this API. Otherwise, you must have Source-File 7 in
order to use this API in other BitNodes order to use this API in other BitNodes
**Bladeburner API functions must be accessed through the bladeburner namespace** **Bladeburner API functions must be accessed through the 'bladeburner' namespace**
In Netscript 1.0:: In :ref:`netscript1`::
bladeburner.getContractNames(); bladeburner.getContractNames();
bladeburner.startAction("general", "Training"); bladeburner.startAction("general", "Training");

@ -0,0 +1,74 @@
.. _netscriptcodingcontractapi:
Netscript Coding Contract API
=============================
Netscript provides the following API for interacting with
:ref:`codingcontracts`.
**The Coding Contract API must be accessed through the 'codingcontract' namespace**
In :ref:`netscript1`::
codingcontract.getDescription("foo.cct", "home");
codingcontract.attempt(1, "foo.cct", "foodnstuff");
In :ref:`netscriptjs`::
ns.codingcontract.getDescription("foo.cct", "home");
ns.codingcontract.attempt(1, "foo.cct", "foodnstuff");
attempt
-------
.. js:function:: attempt(answer, fn[, hostname/ip=current ip])
:param answer: Solution for the contract
:param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided
Attempts to solve the Coding Contract with the provided solution.
:returns: Boolean indicating whether the solution was correct
getDescription
--------------
.. js:function:: getDescription(fn[, hostname/ip=current ip])
:param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided
Get the full text description for the problem posed by the Coding Contract
:returns: A string with the contract's text description
getData
-------
.. js:function:: getData(fn[, hostname/ip=current ip])
:param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided
Get the data associated with the specific Coding Contract. Note that this is
not the same as the contract's description. This is just the data that
the contract wants you to act on in order to solve
:returns: The specified contract's data
getNumTriesRemaining
--------------------
.. js:function:: getNumTriesRemaining(fn[, hostname/ip=current ip])
:param string fn: Filename of the contract
:param string hostname/ip: Hostname or IP of the server containing the contract.
Optional. Defaults to current server if not provided
Get the number of tries remaining on the contract before it
self-destructs.
:returns: Number indicating how many attempts are remaining

@ -154,7 +154,7 @@ getScriptLogs
scan scan
^^^^ ^^^^
.. js:function:: scan(hostname/ip[, hostnames=true]) .. js:function:: scan(hostname/ip=current ip[, hostnames=true])
:param string hostname/ip: IP or hostname of the server to scan :param string hostname/ip: IP or hostname of the server to scan
:param boolean: Optional boolean specifying whether the function should output hostnames (if true) or IP addresses (if false) :param boolean: Optional boolean specifying whether the function should output hostnames (if true) or IP addresses (if false)

@ -285,12 +285,15 @@ except literature files (.lit).
**WARNING: This is permanent and cannot be undone** **WARNING: This is permanent and cannot be undone**
.. _run_terminal_command:
run run
^^^ ^^^
$ run [file name] [-t] [num threads] [args...] $ run [file name] [-t] [num threads] [args...]
Execute a program or a script. Execute a program, script, or :ref:`codingcontracts`.
The '[-t]', '[num threads]', and '[args...]' arguments are only valid when The '[-t]', '[num threads]', and '[args...]' arguments are only valid when
running a script. The '-t' flag is used to indicate that the script should running a script. The '-t' flag is used to indicate that the script should
@ -305,13 +308,17 @@ argument must be separated by a space.
**Examples** **Examples**
Run a program: Run a program::
run BruteSSH.exe $ run BruteSSH.exe
Run *foo.script* with 50 threads and the arguments [1e3, 0.5, foodnstuff]:: Run *foo.script* with 50 threads and the arguments [1e3, 0.5, foodnstuff]::
run foo.script -t 50 1e3 0.5 foodnstuff $ run foo.script -t 50 1e3 0.5 foodnstuff
Run a Coding Contract::
$ run foo-contract.cct
scan scan
^^^^ ^^^^

@ -78,7 +78,7 @@ let NetscriptFunctions =
"getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" + "getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" +
"getTimeSinceLastAug|prompt|" + "getTimeSinceLastAug|prompt|" +
//Singularity Functions // Singularity Functions
"universityCourse|getCharacterInformation|" + "universityCourse|getCharacterInformation|" +
"gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" + "gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" +
"getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" + "getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" +
@ -91,16 +91,16 @@ let NetscriptFunctions =
"getAugmentationCost|purchaseAugmentation|" + "getAugmentationCost|purchaseAugmentation|" +
"installAugmentations|" + "installAugmentations|" +
//TIX API // TIX API
"getStockPrice|getStockPosition|buyStock|sellStock|shortStock|sellShort|" + "getStockPrice|getStockPosition|buyStock|sellStock|shortStock|sellShort|" +
"placeOrder|cancelOrder|" + "placeOrder|cancelOrder|" +
//Hacknet Node API // Hacknet Node API
"hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" + "hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" +
"upgradeLevel|upgradeRam|upgradeCore|getLevelUpgradeCost|" + "upgradeLevel|upgradeRam|upgradeCore|getLevelUpgradeCost|" +
"getRamUpgradeCost|getCoreUpgradeCost|" + "getRamUpgradeCost|getCoreUpgradeCost|" +
//Bladeburner functions // Bladeburner functions
"bladeburner|getContractNames|getOperationNames|getBlackOpNames|" + "bladeburner|getContractNames|getOperationNames|getBlackOpNames|" +
"getGeneralActionNames|getSkillNames|startAction|stopBladeburnerAction|" + "getGeneralActionNames|getSkillNames|startAction|stopBladeburnerAction|" +
"getActionTime|getActionEstimatedSuccessChance|getActionCountRemaining|" + "getActionTime|getActionEstimatedSuccessChance|getActionCountRemaining|" +
@ -109,7 +109,10 @@ let NetscriptFunctions =
"getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" + "getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" +
"upgradeSkill|getTeamSize|getCity|" + "upgradeSkill|getTeamSize|getCity|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" + "setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime"; "getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime|" +
// Coding Contract API
"codingcontract|attempt|getData|getDescription|getNumTriesRemaining";
var NetscriptHighlightRules = function(options) { var NetscriptHighlightRules = function(options) {
var keywordMapper = this.createKeywordMapper({ var keywordMapper = this.createKeywordMapper({

@ -70,7 +70,7 @@ export enum CodingContractRewardType {
FactionReputation, FactionReputation,
FactionReputationAll, FactionReputationAll,
CompanyReputation, CompanyReputation,
Money, // This must always be the last reward type Money, // This must always be the last reward type
} }
/** /**
@ -137,6 +137,14 @@ export class CodingContract {
this.reward = reward; this.reward = reward;
} }
getData(): any {
return this.data;
}
getDescription(): string {
return CodingContractTypes[this.type].desc(this.data);
}
getDifficulty(): number { getDifficulty(): number {
return CodingContractTypes[this.type].difficulty; return CodingContractTypes[this.type].difficulty;
} }
@ -155,15 +163,21 @@ export class CodingContract {
async prompt(): Promise<CodingContractResult> { async prompt(): Promise<CodingContractResult> {
// tslint:disable-next-line // tslint:disable-next-line
return new Promise<CodingContractResult>((resolve: Function, reject: Function) => { return new Promise<CodingContractResult>((resolve: Function, reject: Function) => {
const contractType: ContractType = CodingContractTypes[this.type]; const contractType: CodingContractType = CodingContractTypes[this.type];
const popupId: string = `coding-contract-prompt-popup-${this.fn}`; const popupId: string = `coding-contract-prompt-popup-${this.fn}`;
const txt: HTMLElement = createElement("p", { const txt: HTMLElement = createElement("p", {
innerText: ["You are attempting to solve a Coding Contract. Note that", innerText: ["You are attempting to solve a Coding Contract. You have",
"you only have one chance. Providing the wrong solution", `${this.getMaxNumTries() - this.tries} tries remaining,`,
"will cause the contract to self-destruct.\n\n", "after which the contract will self-destruct.\n\n",
`${contractType.desc(this.data)}`].join(" "), `${contractType.desc(this.data)}`].join(" "),
}); });
const answerInput: HTMLInputElement = createElement("input", { const answerInput: HTMLInputElement = createElement("input", {
onkeydown:(e)=>{
if (e.keyCode === 13 && answerInput.value !== "") {
e.preventDefault();
solveBtn.click();
}
},
placeholder: "Enter Solution here", placeholder: "Enter Solution here",
}) as HTMLInputElement; }) as HTMLInputElement;
const solveBtn: HTMLElement = createElement("a", { const solveBtn: HTMLElement = createElement("a", {
@ -189,6 +203,7 @@ export class CodingContract {
}); });
const lineBreak: HTMLElement = createElement("br"); const lineBreak: HTMLElement = createElement("br");
createPopup(popupId, [txt, lineBreak, lineBreak, answerInput, solveBtn, cancelBtn]); createPopup(popupId, [txt, lineBreak, lineBreak, answerInput, solveBtn, cancelBtn]);
answerInput.focus();
}); });
} }

@ -85,8 +85,7 @@ let CONSTANTS = {
ScriptGetScriptRamCost: 0.1, ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05, ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10, ScriptGetFavorToDonate: 0.10,
ScriptGetContractDataRamCost: 25, ScriptCodingContractBaseRamCost:10,
ScriptAttemptContractRamCost: 25,
ScriptSingularityFn1RamCost: 1, ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2, ScriptSingularityFn2RamCost: 2,
@ -274,7 +273,7 @@ let CONSTANTS = {
/* Coding Contract Constants */ /* Coding Contract Constants */
CodingContractBaseFactionRepGain: 2500, CodingContractBaseFactionRepGain: 2500,
CodingContractBaseCompanyRepGain: 4000, CodingContractBaseCompanyRepGain: 4000,
CodingContractBaseMoneyGain: 10e6, CodingContractBaseMoneyGain: 50e6,
/* Tutorial related things */ /* Tutorial related things */
TutorialNetworkingText: "Servers are a central part of the game. You start with a single personal server (your home computer) " + TutorialNetworkingText: "Servers are a central part of the game. You start with a single personal server (your home computer) " +
@ -508,35 +507,13 @@ let CONSTANTS = {
LatestUpdate: LatestUpdate:
` `
v0.40.4<br> v0.40.4<br>
* (TODO NEEDS DOCUMENTATION) The write() and read() Netscript functions now work on scripts<br> * Added new Coding Contracts mechanic. Solve programming problems to earn rewards
* It is now possible to use freely use angled bracket (<, >) and create DOM elements using tprint()<br> * (TODO NEEDS DOCUMENTATION) The write() and read() Netscript functions now work on scripts
* Added Coding Contracts (not yet generated in game, but the data/implementation exists)<br> * It is now possible to use freely use angled bracket (<, >) and create DOM elements using tprint()
* The game's theme colors can now be set through the Terminal configuration (.fconf).
* You can now switch to the old left-hand main menu bar through the Terminal configuration (.fconf)
`
v0.40.3<br>
-----------------------------------------------<br>
* Bladeburner Changes:<br>
*** Increased the effect that agi and dexterity have on action time<br>
*** Starting number of contracts/operations available will be slightly lower<br>
*** Random events will now happen slightly more often<br>
*** Slightly increased the rate at which the Overclock skill point cost increases<br>
-----------------------------------------------<br>
* The maximum volatility of stocks is now randomized (randomly generated within a certain range every time the game resets)<br>
* Increased the range of possible values for initial stock prices<br>
* b1t_flum3.exe program can now be created immediately at Hacking level 1 (rather than hacking level 5)<br>
* UI improvements for the character overview panel and the left-hand menu (by mat-jaworski)<br>
* General UI improvements for displays and Terminal (by mat-jaworski)<br>
* Added optional parameters to the getHackTime(), getGrowTime(), and getWeakenTime() Netscript functions<br>
* Added isLogEnabled() and getScriptLogs() Netscript functions<br>
* Added donateToFaction() Singularity function<br>
* Updated documentation to reflect the fact that Netscript port handles (getPortHandle()) only works in NetscriptJS (2.0), NOT Netscript 1.0<br>
* Added tryWrite() Netscript function<br>
* When working (for a company/faction), experience is gained immediately/continuously rather than all at once when the work is finished<br>
* Added a setting in .fconf for enabling line-wrap in the Terminal input<br>
* Adding a game option for changing the locale that most numbers are displayed in (this mostly applies for whenever money is displayed)<br>
* The randomized parameters of many high-level servers can now take on a higher range of values<br>
* Many 'foreign' servers (hackable servers that you don't own) now have a randomized amount of RAM<br>
* Added 'wget' Terminal command<br>
* Improved the introductory tutorial`
} }
export {CONSTANTS}; export {CONSTANTS};

@ -175,7 +175,21 @@ function NetscriptFunctions(workerScript) {
} }
}; };
//Utility function to get Hacknet Node object /**
* Gets the Server for a specific hostname/ip, throwing an error
* if the server doesn't exist.
* @param {string} Hostname or IP of the server
* @returns {Server} The specified Server
*/
var safeGetServer = function(ip, callingFnName="") {
var server = getServer(ip);
if (server == null) {
throw makeRuntimeRejectMsg(workerScript, `Invalid IP or hostname passed into ${callingFnName}() function`);
}
return server;
}
// Utility function to get Hacknet Node object
var getHacknetNode = function(i) { var getHacknetNode = function(i) {
if (isNaN(i)) { if (isNaN(i)) {
throw makeRuntimeRejectMsg(workerScript, "Invalid index specified for Hacknet Node: " + i); throw makeRuntimeRejectMsg(workerScript, "Invalid index specified for Hacknet Node: " + i);
@ -186,6 +200,11 @@ function NetscriptFunctions(workerScript) {
return Player.hacknetNodes[i]; return Player.hacknetNodes[i];
}; };
var getCodingContract = function(fn, ip) {
var server = safeGetServer(ip, "getCodingContract");
return server.getContract(fn);
}
/** /**
* @param {number} ram The amount of server RAM to calculate cost of. * @param {number} ram The amount of server RAM to calculate cost of.
* @exception {Error} If the value passed in is not numeric, out of range, or too large of a value. * @exception {Error} If the value passed in is not numeric, out of range, or too large of a value.
@ -1057,6 +1076,16 @@ function NetscriptFunctions(workerScript) {
} }
} }
for (var i = 0; i < server.contracts.length; ++i) {
if (filter) {
if (server.contracts[i].fn.includes(filter)) {
allFiles.push(server.contracts[i].fn);
}
} else {
allFiles.push(server.contracts[i].fn);
}
}
//Sort the files alphabetically then print each //Sort the files alphabetically then print each
allFiles.sort(); allFiles.sort();
return allFiles; return allFiles;
@ -2040,6 +2069,13 @@ function NetscriptFunctions(workerScript) {
return true; return true;
} }
} }
} else if (fn.endsWith(".cct")) {
for (var i = 0; i < s.contracts.length; ++i) {
if (s.contracts[i].fn === fn) {
s.contracts.splice(i, 1);
return true;
}
}
} }
return false; return false;
}, },
@ -3947,6 +3983,72 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "getBonusTime() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " + throw makeRuntimeRejectMsg(workerScript, "getBonusTime() failed because you do not currently have access to the Bladeburner API. This is either because you are not currently employed " +
"at the Bladeburner division or because you do not have Source-File 7"); "at the Bladeburner division or because you do not have Source-File 7");
} }
}, // End Bladeburner
codingcontract : {
attempt : function(answer, fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("attempt", CONSTANTS.ScriptCodingContractBaseRamCost);
}
updateDynamicRam("attempt", CONSTANTS.ScriptCodingContractBaseRamCost);
const contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`);
return false;
}
answer = String(answer);
const serv = safeGetServer(ip, "codingcontract.attempt()");
if (contract.isSolution(answer)) {
const reward = Player.gainCodingContractReward(contract.reward, contract.getDifficulty());
workerScript.log(`Successfully completed Coding Contract ${fn}. Reward: ${reward}`);
serv.removeContract(fn);
return true;
} else {
++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) {
workerScript.log(`Coding Contract ${fn} failed. Contract is now self-destructing`);
serv.removeContract(fn);
} else {
workerScript.log(`Coding Contract ${fn} failed. ${contract.getMaxNumTries() - contract.tries} attempts remaining`);
}
return false;
}
},
getData : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
}
updateDynamicRam("getData", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
var contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getData() failed because it could find the specified contract ${fn} on server ${ip}`);
return null;
}
return contract.getData();
},
getDescription : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("getDescription", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
}
updateDynamicRam("getDescription", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
var contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getDescription() failed because it could find the specified contract ${fn} on server ${ip}`);
return "";
}
return contract.getDescription();
},
getNumTriesRemaining : function(fn, ip=workerScript.serverIp) {
if (workerScript.checkingRam) {
return updateStaticRam("getNumTriesRemaining", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
}
updateDynamicRam("getNumTriesRemaining", CONSTANTS.ScriptCodingContractBaseRamCost / 2);
var contract = getCodingContract(fn, ip);
if (contract == null) {
workerScript.log(`ERROR: codingcontract.getNumTriesRemaining() failed because it could find the specified contract ${fn} on server ${ip}`);
return -1;
}
return contract.getMaxNumTries() - contract.tries;
},
} }
} //End return } //End return
} //End NetscriptFunction() } //End NetscriptFunction()

@ -2298,12 +2298,26 @@ PlayerObject.prototype.gainCodingContractReward = function(reward, difficulty=1)
return `Gained ${repGain} faction reputation for ${reward.name}`; return `Gained ${repGain} faction reputation for ${reward.name}`;
case CodingContractRewardType.FactionReputationAll: case CodingContractRewardType.FactionReputationAll:
const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty;
const gainPerFaction = Math.floor(totalGain / this.factions.length);
for (const facName of this.factions) { // Ignore Bladeburners and other special factions for this calculation
const specialFactions = ["Bladeburners"];
var factions = this.factions.slice();
factions = factions.filter((f) => {
return !specialFactions.includes(f);
});
// If the player was only part of the special factions, we'll just give money
if (factions.length == 0) {
reward.type = CodingContractRewardType.Money;
return this.gainCodingContractReward(reward, difficulty);
}
const gainPerFaction = Math.floor(totalGain / factions.length);
for (const facName of factions) {
if (!(Factions[facName] instanceof Faction)) { continue; } if (!(Factions[facName] instanceof Faction)) { continue; }
Factions[facName].playerReputation += gainPerFaction; Factions[facName].playerReputation += gainPerFaction;
} }
return `Gained ${gainPerFaction} reputation for each faction you are a member of`; return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.toString()}`;
break; break;
case CodingContractRewardType.CompanyReputation: case CodingContractRewardType.CompanyReputation:
if (reward.name == null || !(Companies[reward.name] instanceof Company)) { if (reward.name == null || !(Companies[reward.name] instanceof Company)) {

@ -531,10 +531,12 @@ function parseOnlyRamCalculate(server, code, workerScript) {
} }
} }
//Special logic for Bladeburner //Special logic for namespaces (Bladeburner, CodingCOntract)
var func; var func;
if (ref in workerScript.env.vars.bladeburner) { if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref]; func = workerScript.env.vars.bladeburner[ref];
} else if (ref in workerScript.env.vars.codingcontract) {
func = workerScript.env.vars.codingcontract[ref];
} else { } else {
func = workerScript.env.get(ref); func = workerScript.env.get(ref);
} }

@ -458,35 +458,42 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
} }
if (input.startsWith("rm ")) { if (input.startsWith("rm ")) {
for (var i = 0; i < currServ.scripts.length; ++i) { for (let i = 0; i < currServ.scripts.length; ++i) {
allPos.push(currServ.scripts[i].filename); allPos.push(currServ.scripts[i].filename);
} }
for (var i = 0; i < currServ.programs.length; ++i) { for (let i = 0; i < currServ.programs.length; ++i) {
allPos.push(currServ.programs[i]); allPos.push(currServ.programs[i]);
} }
for (var i = 0; i < currServ.messages.length; ++i) { for (let i = 0; i < currServ.messages.length; ++i) {
if (!(currServ.messages[i] instanceof Message) && isString(currServ.messages[i]) && if (!(currServ.messages[i] instanceof Message) && isString(currServ.messages[i]) &&
currServ.messages[i].endsWith(".lit")) { currServ.messages[i].endsWith(".lit")) {
allPos.push(currServ.messages[i]); allPos.push(currServ.messages[i]);
} }
} }
for (var i = 0; i < currServ.textFiles.length; ++i) { for (let i = 0; i < currServ.textFiles.length; ++i) {
allPos.push(currServ.textFiles[i].fn); allPos.push(currServ.textFiles[i].fn);
} }
for (let i = 0; i < currServ.contracts.length; ++i) {
allPos.push(currServ.contracts[i].fn);
}
return allPos; return allPos;
} }
if (input.startsWith("run ")) { if (input.startsWith("run ")) {
//All programs and scripts //All programs, scripts, and contracts
for (var i = 0; i < currServ.scripts.length; ++i) { for (let i = 0; i < currServ.scripts.length; ++i) {
allPos.push(currServ.scripts[i].filename); allPos.push(currServ.scripts[i].filename);
} }
//Programs are on home computer //Programs are on home computer
var homeComputer = Player.getHomeComputer(); var homeComputer = Player.getHomeComputer();
for(var i = 0; i < homeComputer.programs.length; ++i) { for (let i = 0; i < homeComputer.programs.length; ++i) {
allPos.push(homeComputer.programs[i]); allPos.push(homeComputer.programs[i]);
} }
for (let i = 0; i < currServ.contracts.length; ++i) {
allPos.push(currServ.contracts[i].fn);
}
return allPos; return allPos;
} }
@ -1341,6 +1348,13 @@ let Terminal = {
return; return;
} }
} }
} else if (delTarget.endsWith(".cct")) {
for (var i = 0; i < s.contracts.length; ++i) {
if (s.contracts[i].fn === delTarget) {
s.contracts.splice(i, 1);
return;
}
}
} }
post("Error: No such file exists"); post("Error: No such file exists");
break; break;
@ -2154,13 +2168,14 @@ let Terminal = {
if (Terminal.contractOpen) { if (Terminal.contractOpen) {
return post("ERROR: There's already a Coding Contract in Progress"); return post("ERROR: There's already a Coding Contract in Progress");
} }
Terminal.contractOpen = true;
const serv = Player.getCurrentServer(); const serv = Player.getCurrentServer();
const contract = serv.getContract(contractName); const contract = serv.getContract(contractName);
if (contract == null) { if (contract == null) {
return post("ERROR: No such contract"); return post("ERROR: No such contract");
} }
Terminal.contractOpen = true;
const res = await contract.prompt(); const res = await contract.prompt();
switch (res) { switch (res) {
@ -2170,10 +2185,12 @@ let Terminal = {
serv.removeContract(contract); serv.removeContract(contract);
break; break;
case CodingContractResult.Failure: case CodingContractResult.Failure:
post("Contract <p style='color:red;display:inline'>FAILED</p> - Contract is now self-destructing");
++contract.tries; ++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) { if (contract.tries >= contract.getMaxNumTries()) {
post("Contract <p style='color:red;display:inline'>FAILED</p> - Contract is now self-destructing");
serv.removeContract(contract); serv.removeContract(contract);
} else {
post(`Contract <p style='color:red;display:inline'>FAILED</p> - ${contract.getMaxNumTries() - contract.tries} tries remaining`);
} }
break; break;
case CodingContractResult.Cancelled: case CodingContractResult.Cancelled:
@ -2182,6 +2199,7 @@ let Terminal = {
break; break;
} }
Terminal.contractOpen = false; Terminal.contractOpen = false;
console.log(Terminal.contractOpen);
}, },
}; };

@ -21,6 +21,28 @@ export interface ICodingContractTypeMetadata {
solver: SolverFunc; solver: SolverFunc;
} }
/* Helper functions for Coding Contract implementations */
function removeBracketsFromArrayString(str: string) {
let strCpy: string = str;
if (strCpy.startsWith("[")) { strCpy = strCpy.slice(1); }
if (strCpy.endsWith("]")) { strCpy = strCpy.slice(-1); }
return strCpy;
}
function convert2DArrayToString(arr: any[][]) {
let res = "";
const components: string[] = [];
arr.forEach((e) => {
let s = e.toString();
s = ["[", s, "]"].join("");
components.push(s);
});
res = components.join(",");
return res.replace(/\s/g, "");
}
export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
{ {
desc: (n: number) => { desc: (n: number) => {
@ -136,6 +158,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
const matrix: number[][] = []; const matrix: number[][] = [];
matrix.length = m; matrix.length = m;
for (let i: number = 0; i < m; ++i) { for (let i: number = 0; i < m; ++i) {
matrix[i] = [];
matrix[i].length = n; matrix[i].length = n;
} }
@ -187,7 +210,9 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
} }
if (++l > r) { break; } if (++l > r) { break; }
} }
const playerAns: any[] = ans.split(",");
const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
const playerAns: any[] = sanitizedPlayerAns.split(",");
for (let i: number = 0; i < playerAns.length; ++i) { for (let i: number = 0; i < playerAns.length; ++i) {
playerAns[i] = parseInt(playerAns[i], 10); playerAns[i] = parseInt(playerAns[i], 10);
} }
@ -282,22 +307,21 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
} }
result.push([start, end]); result.push([start, end]);
const sanitizedResult: string = result const sanitizedResult: string = convert2DArrayToString(result);
.toString()
.replace(/\s/g, "");
const sanitizedAns: string = ans.replace(/\s/g, ""); const sanitizedAns: string = ans.replace(/\s/g, "");
return sanitizedResult === sanitizedAns; return (sanitizedResult === sanitizedAns ||
sanitizedResult === removeBracketsFromArrayString(sanitizedAns));
}, },
}, },
{ {
desc: (data: string) => { desc: (data: string) => {
return ["Given the following string containing only digits, determine", return ["Given the following string containing only digits, return",
"an array with all possible valid IP address combinations", "an array with all possible valid IP address combinations",
"that can be created from the string:\n\n", "that can be created from the string:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Example:\n\n", "Example:\n\n",
"'25525511135' -> ['255.255.11.135', '255.255.111.35']"].join(" "); "25525511135 -> [255.255.11.135, 255.255.111.35]"].join(" ");
}, },
difficulty: 3, difficulty: 3,
gen: () => { gen: () => {
@ -425,7 +449,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
return ["You are given the following array of stock prices where the i-th element", return ["You are given the following array of stock prices where the i-th element",
"represents the stock price on day i:\n\n", "represents the stock price on day i:\n\n",
`${data}\n\n`, `${data}\n\n`,
"Determine the maximum possible profit you can earn using at most ", "Determine the maximum possible profit you can earn using at most",
"two transactions. A transaction is defined as buying", "two transactions. A transaction is defined as buying",
"and then selling one share of the stock. Note that you cannot", "and then selling one share of the stock. Note that you cannot",
"engage in multiple transactions at once. In other words, you", "engage in multiple transactions at once. In other words, you",

@ -917,7 +917,6 @@ const Engine = {
}, },
updateGame: function(numCycles = 1) { updateGame: function(numCycles = 1) {
//Update total playtime
var time = numCycles * Engine._idleSpeed; var time = numCycles * Engine._idleSpeed;
if (Player.totalPlaytime == null) {Player.totalPlaytime = 0;} if (Player.totalPlaytime == null) {Player.totalPlaytime = 0;}
if (Player.playtimeSinceLastAug == null) {Player.playtimeSinceLastAug = 0;} if (Player.playtimeSinceLastAug == null) {Player.playtimeSinceLastAug = 0;}
@ -1155,20 +1154,66 @@ const Engine = {
} }
if (Engine.Counters.contractGeneration <= 0) { if (Engine.Counters.contractGeneration <= 0) {
// 5% chance of a contract being generated // 20% chance of a contract being generated
if (Math.random() <= 0.05) { if (Math.random() < 0.2) {
// First select a random problem type // First select a random problem type
let problemTypes = Object.keys(CodingContractTypes); const problemTypes = Object.keys(CodingContractTypes);
let randIndex = getRandomInt(0, problemTypes.length - 1); let randIndex = getRandomInt(0, problemTypes.length - 1);
let problemType = CodingContractTypes[problemTypes[randIndex]]; let problemType = problemTypes[randIndex];
// Then select a random reward type. 'Money' will always be the last reward type // Then select a random reward type. 'Money' will always be the last reward type
let rewardType = getRandomInt(1, CodingContractRewardType.Money); var reward = {};
reward.type = getRandomInt(0, CodingContractRewardType.Money);
// Change type based on certain conditions
if (Player.factions.length === 0) { reward.type = CodingContractRewardType.CompanyReputation; }
if (Player.companyName === "") { reward.type = CodingContractRewardType.Money; }
// Add additional information based on the reward type // Add additional information based on the reward type
switch (rewardType) { switch (reward.type) {
case CodingContractRewardType case CodingContractRewardType.FactionReputation:
// Get a random faction that player is a part of. That
//faction must allow hacking contracts
var numFactions = Player.factions.length;
var randFaction = Player.factions[getRandomInt(0, numFactions - 1)];
try {
while(Factions[randFaction].getInfo().offerHackingWork !== true) {
randFaction = Player.factions[getRandomInt(0, numFactions - 1)];
}
reward.name = randFaction;
} catch (e) {
exceptionAlert("Failed to find a faction for Coding Contract Generation: " + e);
}
break;
case CodingContractRewardType.CompanyReputation:
if (Player.companyName !== "") {
reward.name = Player.companyName;
} else {
reward.type = CodingContractRewardType.Money;
}
break;
default:
break;
} }
// Choose random server
const servers = Object.keys(AllServers);
randIndex = getRandomInt(0, servers.length - 1);
var randServer = AllServers[servers[randIndex]];
while (randServer.purchasedByPlayer === true) {
randIndex = getRandomInt(0, servers.length - 1);
randServer = AllServers[servers[randIndex]];
}
let contractFn = `contract-${getRandomInt(0, 1e6)}`;
while (randServer.contracts.filter((c) => {return c.fn === contractFn}).length > 0) {
contractFn = `contract-${getRandomInt(0, 1e6)}`;
}
if (reward.name) { contractFn += `-${reward.name.replace(/\s/g, "")}`; }
let contract = new CodingContract(contractFn, problemType, reward);
randServer.addContract(contract);
} }
Engine.Counters.contractGeneration = 3000; Engine.Counters.contractGeneration = 3000;
} }

@ -35,6 +35,7 @@ interface ICreateElementListenerOptions {
clickListener?(this: HTMLElement, ev: MouseEvent): any; clickListener?(this: HTMLElement, ev: MouseEvent): any;
inputListener?(this: HTMLElement, ev: Event): any; inputListener?(this: HTMLElement, ev: Event): any;
onfocus?(this: HTMLElement, ev: FocusEvent): any; onfocus?(this: HTMLElement, ev: FocusEvent): any;
onkeydown?(this: HTMLElement, ev: KeyboardEvent): any;
onkeyup?(this: HTMLElement, ev: KeyboardEvent): any; onkeyup?(this: HTMLElement, ev: KeyboardEvent): any;
} }
@ -148,6 +149,9 @@ function setElementListeners(el: HTMLElement, params: ICreateElementListenerOpti
if (params.onkeyup !== undefined) { if (params.onkeyup !== undefined) {
el.addEventListener("keyup", params.onkeyup); el.addEventListener("keyup", params.onkeyup);
} }
if (params.onkeydown !== undefined) {
el.addEventListener("keydown", params.onkeydown);
}
if (params.onfocus !== undefined) { if (params.onfocus !== undefined) {
el.addEventListener("focus", params.onfocus); el.addEventListener("focus", params.onfocus);
} }