As an avid Pokemon Showdown player, this caught my eye:
> In the case of Pokémon Showdown, only bugs which stem from a misimplementation of specific effects are reproduced in the engine, bugs which are the result of a misunderstanding of the fundamental mechanics of Pokémon or which simply arise due to specific Pokémon Showdown implementation details that aren't replicable without making the same (incorrect) architectural choices aren't.
Very curious what "incorrect architecture choices" refers to but I assume the post-Dexit NatDex clusterfuck is a major part of it, lol. I do think "Assist can call any move it's not explicitly forbidden from calling, even moves that were never present in the same game as Assist" was the wrong choice, for example, and we saw how that played out with ReviveCats :P
Pokémon Showdown was written to simulate interactions that occur in Generation V (which was modern at the time) based on the (very rough) understanding of mechanics that we had 15 years ago. Every other generation was then shoehorned to fit into this architecture (which isn't actually even correct for Generation V). While this kind of works for modern generations, Generations I and II were quite a bit simpler. e.g., what Pokémon Showdown deems as the "residual phase" does not exist in Generation I & II, and this results in an incorrect ordering of events compared to the cartridge implementations. https://github.com/pkmn/engine/blob/main/src/lib/gen1/README... goes into detail about what Pokémon Showdown-specific behavior is currently unimplementable in Generation I (though binding moves like Wrap have actually been fixed on Pokémon Showdown and there's a branch that should update this section to account for that).
A couple of bugs in gen 1 that might be implemented differently than PS, would be bugs that are fixed in ps due to balancing or due to playability:
- multiplayer desyncs, see counter
- fly/dig para invuln.
- freeze clause, and to a lesser extent sleep clause
Bugs like normal body slam para invuln, amnesia reapplying speed drops, and toxic counter bugs, 1/256 miss chance, focus energy bugs, I would expect to stay. If those get fixed, the project would veer into the terrain of natdex, Create A Pokemon, or Custom Metas, as in made up fan games that never existed in cartridge
Yeah I was wondering about desyncs too, because that's a case where replicating "authentic" cartridge behavior doesn't really add any value from a player perspective. Invulnerability exploits are fun if you're the one taking advantage of them but it's obviously less fun to be on the receiving end, lol.
ETA: Not directly related, but @ the creator of this tool: if you're planning to implement more gens, you'll be in for a fun time with Sheer Force interactions!
This is strange - this post is on the front page right now. It says that I submitted it eight hours ago. I was asleep eight hours ago. I submitted this last week and it did not get any points hardly but now it is on the front page with an incorrect timestamp but still attributed to my user account.
I think the second chance pool is great but why rewrite the metadata with false info? It just confuses everyone. This isn't the first, and it won't be the last.
Someone else must have submitted the same link, which resurrected your original submission. I'm not sure what the time-limit is for duplicate submissions to become resurrections, but the second submission will be transformed into an upvote for the original from the second submitter and resetting the submission date.
No? When I've inadvertently submitted duplicates before, my submitted dupe was not ever in the article queue, it was immediately and automatically changed into an upvote for the original.
But that doesn't reset the submission date, its just an upvote. Second-chance pool rewrites the timestamp (and thus boosts a post up the ranking, since age is a big factor in the ranking algorithm), and has nothing to do with a second user submitting it.
It is by design as I understand it, to make it look fresh after given a "second chance"? It gave me some surreal deja vus before I realized what was going on when I saw I comment I my self had written and knew when it was actually written.
I like that it has a C API (although it uses opaque data without any real way to initialize it other than just storing it in the program, which doesn't seems like very good to me), and had wanted that before, although I would have preferred one that is written fully in C rather than Zig. (Actually, I did start to write some ideas (including some parts of a .h file) about the structures and functions that the C API should use (although they can be changed if at any time during the design, they are found to be not good). My design is different from that one in many ways.)
I would also want support for some variants (including custom rules) as well as for standard rules, and for team validation (including custom team validation). (I did have ideas also about the rule sets formats, with many options; for some kind of custom rules, it would be necessary to modify the program, but that is OK; some kinds will not require such a modification.)
I also wanted to support Unown's letter in generation II only, and Spinda's spots in some generations (probably III, IV, and V; they would result in some of the data being revealed to the opponent), and the ability to change the number of PP Up (as far as I know, Pokemon Showdown always has the number of PP Up set to 3 and it cannot be changed; it is probably uncommon that you would want to change it anyways but it does have some uses).
Another consideration to be made is: In a double battle, if your second pokemon is prohibited from switching out due to an opponent's ability that you are unaware of (e.g. since it is a pokemon that can have multiple possible abilities, or if it is actually Zoroark), but your first pokemon is immune to that effect (e.g. due to being Ghost type), then it is possible, if the commands are selected for the first pokemon and then for the second pokemon in that order, that you will not be aware of this right away. If your second pokemon tries to switch out, then you will receive an error message and both of your pokemons can reselect their commands, but if your second pokemon tries to switch out and it is actually allowed, then you cannot reselect your commands for either pokemon. (I don't know if this rule is specific to some generations.)
The C API provided here is unquestionably anemic, though perhaps makes more sense when understood to exist more to facilitate bindings written in other languages (Python, C++, Rust, etc) and not as an API intended to be used by developers setting out to write an application in C. The two main reasons the C API kind of sucks is that (1) C does not support namespaces/modularization (2) C does not have convenient standardized bitfield support that works cross platform. Finally, I can't really imagine someone wanting to leverage this engine from C and balking at spending an afternoon writing a friendlier wrapper API specific to their use case. That being said, if you have suggestions for improvements please open an issue on GitHub to discuss it.
Team validation is an an orthogonal problem, and is almost completely solved by PKHeX (https://github.com/kwsch/PKHeX) which already does a better job at it than the original game developers. Pokémon Showdown's custom rules are almost all enforced at team validation time, and the standard clauses/rules which require modifications to the engine (e.g., EBC, Sleep/Freeze Mod, Desync Mod) are all supported already by this engine when -Dshowdown compatibility mode is enabled. Your other requests (Unown/Spinda/PP UPs) are all client concerns and thus would be implemented at a higher level than the engine shared here. Your switching scenario will also be handled whenever the generations it is relevant to get implemented. Pokémon Showdown already supports the concept of "maybe trapped" so presumably handles the scenario you're detailing, though if not I'm sure they would appreciate being made aware of any bugs that might exist.
While there are problems with the C programming language, in my opinion the other programming languages that they tried to make it better, often has their own problems instead, so I still find C to be better anyways.
I probably would not expect to use your program, although I had these suggestions anyways, since it would make sense to be improved even if other implementations also exist. However, I do think that your program does many things better than Pokemon Showdown does (I dislike much of the design of Pokemon Showdown), and the stuff you have written may be helpful when making a different one, if as you say, there are problems with the implementation in Pokemon Showdown.
Having a separate structure for active and inactive pokemons is good that what you have, and is what I had thought should be done also and, reading your documentation, I found out that Pokemon Showdown doesn't do that.
If I write one, I also would want to be more targeted in scope than the official games and Pokemon Showdown, although my scope would be a bit more than you have. What you did may be OK for your uses, although not quite what I wanted to make. I also intended to be more flexible in some ways; this might be slower than yours but would probably be faster than Pokemon Showdown.
> Team validation is an an orthogonal problem, and is almost completely solved by PKHeX
OK, although PKHeX is now another different programming language (C#), and the programs have their own dependencies (in that case, C# 13 and .NET 9.0), while your program also has the dependencies (a Zig compiler, which must be older than a specific version, and also JavaScript code).
> Your other requests (Unown/Spinda/PP UPs) are all client concerns
PP Up does affect the team definition and the game behaviour (it affects such things as e.g. whether or not Struggle is a legal selection). However, at least in the case of generation I, this might not be relevant if you allow starting the battle without being fully healed (if, as you mention, your library does not do team validation). For some later generations, it might be significant if PP can be recovered during battle in some circumstances.
If your program expects the client to decide whether or not teams are revealed, then you could say that my things about Unown and Spinda are client concerns, although I would have the battle engine to
specify by the API that some things are exposed, in order that the caller does not have to know what is supposed to be exposed according to the specified rules.
> our switching scenario will also be handled whenever the generations it is relevant to get implemented. Pokémon Showdown already supports the concept of "maybe trapped" so presumably handles the scenario you're detailing
There are actually a handful of great libraries to help with RL/LLM agents in Pokemon Showdown.
I did a solo hackathon project [0] using poke-env[1] and found it to be pretty easy to get started with. Hoping for an easy-to-use API with this as well.
I had been working on a type combo strength calculation and made my own rudimentary simulator in the process. If only I'd seen this project earlier lol. Not that I need this level of accuracy for my case.
Combining Zig and TypeScript sure seems like a funky way of developing software, but on the other hand it does expose the API to a wide variety of FFIs (with browsers being a major one here). I wonder if the performance numbers stay up like that as the project matures and actual game logic gets implemented.
I'm a bit surprised to see a patch for the Zig compiler itself in the source tree, though. I thought manually patching Zig bugs was no longer necessary?
Most existing Pokémon projects are written in JS/TS, Python, or C#. This engine is targeted primarily towards AI projects (https://pkmn.ai/projects/) where speed is of the utmost importance, hence a low level language like Zig shines (C/C++/Rust were also considered, Zig was actually the last choice here, it just ended up being the best one). TypeScript is convenient because its a decent scripting/glue language and plays well with the existing Pokémon Showdown codebase for easy integration testing.
The performance numbers for Generation I will not change as other generations get implemented (by design), and Pokémon Showdown will likely fare better as the game logic gets more complex but I would still expect at least a 1000x difference in performance even for modern generations - the things that make Pokémon Showdown slow do not go away as the generations increase, there is just perhaps less irrelevant stuff happening in later generations to slow things down.
The Zig patch in the source tree is optional and for performance reasons... but a ~20% performance boost is hard to pass up. This is not a general purpose patch, it is only relevant to this project which makes heavy use of sub-byte integers and has a very very comprehensive testing suite to ensure that the undefined behavior that could occur if this patch were applied and used to compile arbitrary Zig problems are not an issue.
What I plan to do with it isn't important - thus the beauty of the license. MIT gives me freedom without forcing me into a certain source availability philosophy.
I get that permissive licenses are popular now, but “freedom to make sure other people don’t have freedom” still doesn’t feel like a compelling argument.
In specific cases, they make sense purely practically; but generally? It feels like a huge error for the whole world to leave copyleft and the GPL behind. The reason we can run Linux on everything is because Torvalds chose the GPL, and so vendors are forced to share their drivers if they use the kernel. And that is a truly magnificent achievement.
GPL makes sense in a context where that sort of vendor compatibility creates existential conflicts for the product. For an operating system and associated toolset, it is clearly an excellent license and does great work encouraging the necessary cooperation from otherwise unwilling partners.
Games are a different beast. For one, the dominant revenue model for videogames continues to be to sell copies of the software, and most digital platforms want (understandably) to apply some sort of DRM to those purchases. If I license my game library for GPL it means that, among other things, users are not free to use it in their iOS, Android, Nintendo, PS4, Xbox, or Steam* releases. That makes the library dead on arrival for almost all of the compelling distribution channels. As a result, all of my game libraries are MIT. I want users to be able to sell software that is built using my library, and that means I want to use the most permissive library possible.
There is a place to fight philosophical grounds, and then separately there is wanting to make sure that my actual users (game developers) aren't restricted by my personal philosophies. Thankfully, MIT is itself perfectly copyleft compatible. You can very well include it in your GPL games, and I'd love to hear if you do! But if you are the much more typical developer trying to make a living with your craft, then I want to support the practical realities of your available storefront options, and let you get on with that.
(*Someone much better versed in license legalese informed me that Steam games can be released under GPL so long as they avoid the Steam SDK. I am not lawyer-y enough to confirm this or speak to the details.)
If I make a game with SDL then I am not the user of SDL, I am an intermediary between the people who develop SDL and the player who eventually uses SDL in interacting with the game. Calling people who distribute libraries with their programs users instead of reserving that term for the people who actually use the programs with the included libraries is just wrong. If a farmer sells oranges to a supermarket and the supermarket then puts those oranges in baskets together with other fruits and then sells those fruit baskets you wouldn't call the supermarket the consumer of those oranges.
Does it make sense to release everything as GPL, while simultaneously offering a non-gpl option for like $$?
Developers get an unlimited free use trial to try your code, up until release. At which point they can decide to either contribute back with either their code or their money.
> It feels like a huge error for the whole world to leave copyleft and the GPL behind.
I think GPL is great for certain projects and goals.
There are many times where my goal isn’t to retain control, though. I just want to help and have other people use some of my code some times. I don’t really care if it’s an individual working on a hobby project or a giant corporation putting it in their product. My little 1-person open source contribution isn’t going to be the make or break thing to some megacorp’s success or failure.
I think it’s great for Linux. Doesn’t need to be applied to everything though.
It doesn't. Nobody's freedom to use the original MIT code is taken away. Regarding my derivative work, if I never grant it to begin with, there is nothing to be taken away.
I would love an ncurses battle simulator where you would choose
the monsters with perfect EV and IV's (maxed stats) with any attack
the monster supports (either by level or TM's) and you would
just fight against a series of trainers a la Battle Tower or similar.
You would get the actual fun of Pokémon battles without grinding. And, yes, I know emulators with fast forward and so on, but even Pokémon Radical Red has mechanics on purpose to send grinding to /dev/null with dumb battles against Audinos in order to get EXP like craze.
BTW, Pokémon Unbound might be the best hackrom ever because of the environment and features, but when you just want to fight, an straight offline simulator would be much better.
When you’re creating a turn based battling game do you inevitably start off with engines like this to help balance out what various stats and variables should be, to avoid dominant strategies from emerging?
Probably not, since game design is usually done iteratively, and you’ll end up writing a simulator for an unstable ruleset. The first trigger for changing the rules will be whether it’s boring or not, and that will mostly come from playtesting.
Once the system is settled, you might implement a simulator to fix balance issues — but more likely, you’ll use the simulator you already have; the game itself, hopefully running in some kind of headless mode.
Also if you’re planning to continuously update the game for balance (unlike Pokémon), you don’t need to be that worried about dominant strategies (excluding those that are caused by the fundamental ruleset, not just balance numbers) because dominant strategies only matter if the community identifies and utilizes it. So you simply watch what occurs in practice, and sand down the rough edges. The potential degenerate strategies don’t matter until they’re popularized.
I can’t find the article now (I thought it was gaffer but I guess not) but you can also avoid degenerate strategies occurring with escape-hatches. In the article I’m thinking of, in a fighting game they gave every character a “get out of jail free card” by giving a once-per-round X second invulnerability. This meant infinite combos could only be so powerful; obvious and easy to use combos are identified and removed by playtesting, and harder to setup ones that get past playtesting can only do so much harm because they can be invul-cancelled at least once. So they’re betting a counter-strategy will be identified to make it unreliable, and making it additionally unreliable by definition with the escape hatch.
It's certainly not a bad idea, but I'm not sure Pokemon (especially the first generation, like this current implementation) is the best base model to do so—this is a game notorious for its dominant strategies in PvP competitive contexts, unless players agree to certain rules designed to make more strategies viable :P
> In the case of Pokémon Showdown, only bugs which stem from a misimplementation of specific effects are reproduced in the engine, bugs which are the result of a misunderstanding of the fundamental mechanics of Pokémon or which simply arise due to specific Pokémon Showdown implementation details that aren't replicable without making the same (incorrect) architectural choices aren't.
Very curious what "incorrect architecture choices" refers to but I assume the post-Dexit NatDex clusterfuck is a major part of it, lol. I do think "Assist can call any move it's not explicitly forbidden from calling, even moves that were never present in the same game as Assist" was the wrong choice, for example, and we saw how that played out with ReviveCats :P
A couple of bugs in gen 1 that might be implemented differently than PS, would be bugs that are fixed in ps due to balancing or due to playability:
- multiplayer desyncs, see counter - fly/dig para invuln. - freeze clause, and to a lesser extent sleep clause
Bugs like normal body slam para invuln, amnesia reapplying speed drops, and toxic counter bugs, 1/256 miss chance, focus energy bugs, I would expect to stay. If those get fixed, the project would veer into the terrain of natdex, Create A Pokemon, or Custom Metas, as in made up fan games that never existed in cartridge
ETA: Not directly related, but @ the creator of this tool: if you're planning to implement more gens, you'll be in for a fun time with Sheer Force interactions!
Many submissions get overlooked the first time and then get nominated for a second chance.
I would also want support for some variants (including custom rules) as well as for standard rules, and for team validation (including custom team validation). (I did have ideas also about the rule sets formats, with many options; for some kind of custom rules, it would be necessary to modify the program, but that is OK; some kinds will not require such a modification.)
I also wanted to support Unown's letter in generation II only, and Spinda's spots in some generations (probably III, IV, and V; they would result in some of the data being revealed to the opponent), and the ability to change the number of PP Up (as far as I know, Pokemon Showdown always has the number of PP Up set to 3 and it cannot be changed; it is probably uncommon that you would want to change it anyways but it does have some uses).
Another consideration to be made is: In a double battle, if your second pokemon is prohibited from switching out due to an opponent's ability that you are unaware of (e.g. since it is a pokemon that can have multiple possible abilities, or if it is actually Zoroark), but your first pokemon is immune to that effect (e.g. due to being Ghost type), then it is possible, if the commands are selected for the first pokemon and then for the second pokemon in that order, that you will not be aware of this right away. If your second pokemon tries to switch out, then you will receive an error message and both of your pokemons can reselect their commands, but if your second pokemon tries to switch out and it is actually allowed, then you cannot reselect your commands for either pokemon. (I don't know if this rule is specific to some generations.)
Team validation is an an orthogonal problem, and is almost completely solved by PKHeX (https://github.com/kwsch/PKHeX) which already does a better job at it than the original game developers. Pokémon Showdown's custom rules are almost all enforced at team validation time, and the standard clauses/rules which require modifications to the engine (e.g., EBC, Sleep/Freeze Mod, Desync Mod) are all supported already by this engine when -Dshowdown compatibility mode is enabled. Your other requests (Unown/Spinda/PP UPs) are all client concerns and thus would be implemented at a higher level than the engine shared here. Your switching scenario will also be handled whenever the generations it is relevant to get implemented. Pokémon Showdown already supports the concept of "maybe trapped" so presumably handles the scenario you're detailing, though if not I'm sure they would appreciate being made aware of any bugs that might exist.
I probably would not expect to use your program, although I had these suggestions anyways, since it would make sense to be improved even if other implementations also exist. However, I do think that your program does many things better than Pokemon Showdown does (I dislike much of the design of Pokemon Showdown), and the stuff you have written may be helpful when making a different one, if as you say, there are problems with the implementation in Pokemon Showdown.
Having a separate structure for active and inactive pokemons is good that what you have, and is what I had thought should be done also and, reading your documentation, I found out that Pokemon Showdown doesn't do that.
If I write one, I also would want to be more targeted in scope than the official games and Pokemon Showdown, although my scope would be a bit more than you have. What you did may be OK for your uses, although not quite what I wanted to make. I also intended to be more flexible in some ways; this might be slower than yours but would probably be faster than Pokemon Showdown.
> Team validation is an an orthogonal problem, and is almost completely solved by PKHeX
OK, although PKHeX is now another different programming language (C#), and the programs have their own dependencies (in that case, C# 13 and .NET 9.0), while your program also has the dependencies (a Zig compiler, which must be older than a specific version, and also JavaScript code).
> Your other requests (Unown/Spinda/PP UPs) are all client concerns
PP Up does affect the team definition and the game behaviour (it affects such things as e.g. whether or not Struggle is a legal selection). However, at least in the case of generation I, this might not be relevant if you allow starting the battle without being fully healed (if, as you mention, your library does not do team validation). For some later generations, it might be significant if PP can be recovered during battle in some circumstances.
If your program expects the client to decide whether or not teams are revealed, then you could say that my things about Unown and Spinda are client concerns, although I would have the battle engine to specify by the API that some things are exposed, in order that the caller does not have to know what is supposed to be exposed according to the specified rules.
> our switching scenario will also be handled whenever the generations it is relevant to get implemented. Pokémon Showdown already supports the concept of "maybe trapped" so presumably handles the scenario you're detailing
This is OK, then.
I did a solo hackathon project [0] using poke-env[1] and found it to be pretty easy to get started with. Hoping for an easy-to-use API with this as well.
[0] https://x.com/ditzikow/status/1922004651790000521 [1] https://github.com/hsahovic/poke-env
I'm a bit surprised to see a patch for the Zig compiler itself in the source tree, though. I thought manually patching Zig bugs was no longer necessary?
The performance numbers for Generation I will not change as other generations get implemented (by design), and Pokémon Showdown will likely fare better as the game logic gets more complex but I would still expect at least a 1000x difference in performance even for modern generations - the things that make Pokémon Showdown slow do not go away as the generations increase, there is just perhaps less irrelevant stuff happening in later generations to slow things down.
The Zig patch in the source tree is optional and for performance reasons... but a ~20% performance boost is hard to pass up. This is not a general purpose patch, it is only relevant to this project which makes heavy use of sub-byte integers and has a very very comprehensive testing suite to ensure that the undefined behavior that could occur if this patch were applied and used to compile arbitrary Zig problems are not an issue.
Edit: clarified comment on C usage. I wanted to praise the API not the underlying language.
And why does it matter if it's C?
In specific cases, they make sense purely practically; but generally? It feels like a huge error for the whole world to leave copyleft and the GPL behind. The reason we can run Linux on everything is because Torvalds chose the GPL, and so vendors are forced to share their drivers if they use the kernel. And that is a truly magnificent achievement.
Games are a different beast. For one, the dominant revenue model for videogames continues to be to sell copies of the software, and most digital platforms want (understandably) to apply some sort of DRM to those purchases. If I license my game library for GPL it means that, among other things, users are not free to use it in their iOS, Android, Nintendo, PS4, Xbox, or Steam* releases. That makes the library dead on arrival for almost all of the compelling distribution channels. As a result, all of my game libraries are MIT. I want users to be able to sell software that is built using my library, and that means I want to use the most permissive library possible.
There is a place to fight philosophical grounds, and then separately there is wanting to make sure that my actual users (game developers) aren't restricted by my personal philosophies. Thankfully, MIT is itself perfectly copyleft compatible. You can very well include it in your GPL games, and I'd love to hear if you do! But if you are the much more typical developer trying to make a living with your craft, then I want to support the practical realities of your available storefront options, and let you get on with that.
(*Someone much better versed in license legalese informed me that Steam games can be released under GPL so long as they avoid the Steam SDK. I am not lawyer-y enough to confirm this or speak to the details.)
Developers get an unlimited free use trial to try your code, up until release. At which point they can decide to either contribute back with either their code or their money.
I think GPL is great for certain projects and goals.
There are many times where my goal isn’t to retain control, though. I just want to help and have other people use some of my code some times. I don’t really care if it’s an individual working on a hobby project or a giant corporation putting it in their product. My little 1-person open source contribution isn’t going to be the make or break thing to some megacorp’s success or failure.
I think it’s great for Linux. Doesn’t need to be applied to everything though.
Some groups can get rather fussy over the definition of "free", but one can't assume some personal use-case still makes sense a decade from now. =3
But there is https://github.com/pkmn/engine?tab=readme-ov-file#c.
You would get the actual fun of Pokémon battles without grinding. And, yes, I know emulators with fast forward and so on, but even Pokémon Radical Red has mechanics on purpose to send grinding to /dev/null with dumb battles against Audinos in order to get EXP like craze.
BTW, Pokémon Unbound might be the best hackrom ever because of the environment and features, but when you just want to fight, an straight offline simulator would be much better.
Once the system is settled, you might implement a simulator to fix balance issues — but more likely, you’ll use the simulator you already have; the game itself, hopefully running in some kind of headless mode.
Also if you’re planning to continuously update the game for balance (unlike Pokémon), you don’t need to be that worried about dominant strategies (excluding those that are caused by the fundamental ruleset, not just balance numbers) because dominant strategies only matter if the community identifies and utilizes it. So you simply watch what occurs in practice, and sand down the rough edges. The potential degenerate strategies don’t matter until they’re popularized.
I can’t find the article now (I thought it was gaffer but I guess not) but you can also avoid degenerate strategies occurring with escape-hatches. In the article I’m thinking of, in a fighting game they gave every character a “get out of jail free card” by giving a once-per-round X second invulnerability. This meant infinite combos could only be so powerful; obvious and easy to use combos are identified and removed by playtesting, and harder to setup ones that get past playtesting can only do so much harm because they can be invul-cancelled at least once. So they’re betting a counter-strategy will be identified to make it unreliable, and making it additionally unreliable by definition with the escape hatch.
This is probably it. As the article says, it’s also a cleverly layered mechanic if a player correctly predicts when their opponent will use it.
Just picking team rosters, no move sets for first gen is 151 Pokémon, choose 6 =~15 billion potentials.
Zig development experience in a nutshell.