jonesguy14 / footballcoach

Android App for playing/simulating/recruiting as a college football coach
Other
34 stars 8 forks source link

Play Calling and Time Management Restructuring #73

Open destilla opened 8 years ago

destilla commented 8 years ago

Disclaimer: Posting this in issues because I'm proposing a big change and I want feedback on anything in here. These are notes I took in a notebook, but they're barely legible, so I'm putting this here:

Why and Basic Idea

Currently runPlay() doesn't do much to allow the AI to manage time: You basically go for a score if you need to at the end of the game, kick a FG if that will get you what you want, and go onside if necessary. Otherwise, you may end up running a rushing play from the Opponent's 40 with 50 seconds left while down 6.

To help account for this, allow the AI to handle time in a more reasonable manner, and allow for easier future expansion of play styles and types of plays run (a shot to a WR running a Go Route and a throw to the sideline are both passing plays but are done for different reasons and with different results/contexts as a result), I'm proposing breaking runPlay() into 3 distinct sections: Before, During, and After (the play) and modifying it as I outline below.

Additionally, I'm hoping that if I can set this up properly, any contributor can easily jump and follow along with what's going on in runPlay simply, and easily make changes or additions. Right now there are a lot of one off type dealings within the play functions (TDs, picks, fumbles are all handled within their play functions, so any changes that may affect these plays need to be accounted for within each of these plays) and I think if these things are handled in a more centralized way, future updates can be done with less worry of introducing logic errors or bugs

Before the Play

This part of runPlay() is both the area where resetting the programming aspects of a play that would reset from play to play occurs and the representation of where a team would "huddle" before play

A set of play specific variables will be updated/checked as necessary. Some hypothetical examples I thought of:

First: Check the scores, time remaining, and if the clock is running with if/if else statements to determine what sort of game situation the offense is faced with

Next: Check to see if there's a running clock and what the game situation is, then allow time to run off accordingly (a team trying to go fast will burn as few as they can manage, a team trying to milk the clock will burn 25-35)

Finally, use the existing code to figure out if, all else being equal, the team will prefer to rush or pass on the next down (basically get values for preferPass and preferRush)

During the Play

Now that we have some game context and situation setup, let's run whatever play is the best for this situation. The overall goal here will be keeping the existing runPlay() logic intact while expanding on it to allow for different play "tactics" to exist within the same type of play (a run up the middle to kill the clock vs a run to the outside for yardage).

The four main types of plays are: Rushing, Passing, FG, and Punting. FGs and Punts are straight forward, you kick a FG or you punt it.

Rushing and Passing plays can be modified to accept an extra argument, I'm thinking string right now but I don't know if that's the best way to handle it.

Basically the extra argument is where these "play tactics" would come out. So in a two minute drill on 2nd and 9 you would get something like passingPlay(home, away, "two minute") and then within passingPlay() use if statements to determine if there's a play modifier or not. If there is, do something that makes sense (a sideline throw in a two minute, a deep bomb if you're throwing a hail mary), if there isn't, just use the existing passingPlay().

My thoughts is that this will allow for a greater customization of play style when new team strategies are introduced. You want a pass happy but conservative offense? We can have that be one of the checks at the start of the "during" section of runPlay(), have it favor passing plays, but then pass it a modifier that says "hey this is a conservative throw" so that when passingPlay() executes, it executes a pass that is less likely to be picked off, but shorter and less likely to break free. Adding different styles of pass/rush is as simple as thinking about why these scenarios would come into play, adding the necessary checks into runPlay()'s logic, then adding it to the play type's function.

The major change is that things like scores and turnovers are removed from the plays and stuck in the "after" section of runPlay() -- Executing these out of runPlay() ensures that things like getEventPrefix() are handled the same way for each type of TD, Interception, etc (because the down, distance, etc will always be advanced at the same time) so any changes (like when I added freeKick() or college rules OT) are implemented and then changed in one area.

Player specific stats (WR drops, RB fumbles, QB completions, yards gained for all positions, etc.) can still be handeled within the play functions because as far as I know off the top of my head, these deal with local variables that are pushed up to a global variable? I'm not sure and as such don't have any real suggestions for dealing with that. It works very well as it is.

Finally, to account for the fact that a play itself burns time off the clock, each play function should:

Example of what I had in mind for burning time midplay:

Now that the play has been run, we need to resolve whatever the outcome was. If a TD was scored, act on that. If a pick was thrown/a fumble was recovered, changes possesion. Nothing about how these things are done should change, just where they occur. If we just gained/lost some yards on the play, then check the new down and distance (resetting to 1st and 10 if needed). If it's 4th down and you didn't make the line to gain, turn it over on downs.

Currently TDs are handled via passingTD and rushingTD -- This can still be done by checking what sort of play was run in the play function itself and then executing the appropriate td type function.

A thought about getEventPrefix(): Current plays are logged with the time that the play ended and the new score after a scoring play. If we want to keep it this way, we can simply have getEventPrefix()execute before down and distance are updated in this section of runPlay() -- if we want to have the play data reported based on the start of the play in the future, we can stick it near the start and make a couple of work arounds to have the accurate data reported. (I did something like this with my play by play debugger and it works pretty well)

Misc.

Add in "halftime" and a change of possession after the end of the 2nd quarter

akeaswaran commented 8 years ago

I like this idea. After building this out, we could take it and work it into something user-facing to give players some play-calling ability, as has been suggested before on Reddit. However, I fear this may over complicate what is, at its core, a simple game. @jonesguy14, any thoughts on this?

jonesguy14 commented 8 years ago

Yeah I really like the changes outlined here. A lot of the code I wrote for the game simulation is a year+ old, before a bunch of other features (like team strategies) were implemented, so a big change like this is welcome. It will be a lot easier to maintain, add features, and follow along logically with what is happening, so I am 100% on board.

As for storing the game situation, I'm not sure what the best way to do that would be. it could be a bunch of final ints, since we really only need to tell what strat is occuring, like:

private final static int TWO_MINUTE_DRILL = 0;
private final static int MILK_CLOCK = 1;
private final static int ... = 2;

But if we need to pass more than just one thing to the function (so our "situation" could be more complex with more factors), then maybe a different approach is needed.

Also one more thing to add to Miscellaneous (keep forgetting to put this as an issue) would be the ability to return punts/kick offs for touchdowns, since having that would be nice.

akeaswaran commented 8 years ago

I think that, along with these changes for game situation, we should overhaul team strategies and turn them into offensive/defensive "playbooks" (Triple Option, Spread, 3-4, etc). This way, we can suggest plays for the player to call based on what offense they're using.

Something else I just thought of in terms of user play-calling, we could have to also give opponent teams some "plays", then display the strategy they're using for the user so that they can choose plays to best counter it. We could also break that "true play-calling" mechanic off into hard mode, and keep a simpler version for normal mode where the defense doesn't always match up/it doesn't matter what the defense is calling since all plays have the same probability of success.

jonesguy14 commented 8 years ago

Yeah I agree, we should move towards play books. Triple Option, Air Raid, etc. And I think with these changes making play books would be easier.

akeaswaran commented 8 years ago

For the game situations, I think we would have to develop a set of common situations we want to support, define them clearly, and then write a method checking if the current game conditions (possession, time, yard line, etc) qualify for any of them. We'd also have to nail down a certain set of plays we want to run under a given situation and pass that along as well.

In theory, we could declare private final static int TWO_MINUTE_DRILL = 0; at the top of the file, then later have something like this:

public int checkGameSituation(Team offense) {
    if (gameTime <= 120 && Math.abs(homeScore - awayScore) <= 7) {
        return TWO_MINUTE_DRILL;
    } else if ...

The catch here is that we have to define these situations (how ever many we decide to have) and their ensuing plays very specifically. Something like:

and so on and so forth for all of the situations we want to cover.

destilla commented 8 years ago

I think @akeaswaran makes a good point that if we introduce too many checks with the user that we may stray away from the core of what makes this game great as it is. One way to handle an eventual play calling system in the future would be to have two options for each week: Coach Week and Simulate Week.

Coach lets you influence play style, call your timeouts at certain points, elect to go for 2, whatever.

Simulate would be what currently exists.

That way people can choose the kind of experience they want: Those that enjoy the simplicity don't lose it, those that want to go another step have the option to.

I think checkGameSituation() is a great idea for handling what could end up being a very clunky block of code if it was stuck into runPlay() and also agree that we should probably very clearly define when each situation occurs and what should occur.

To start, I was just going to add in clock milk, two minute, and kneel down situations so that we have a few very basic situations setup as a template for future ideas. From there I figure we can iteratively add whatever else we come up with.

jonesguy14 commented 8 years ago

Yeah @destilla that is what I would lean towards, if we were to make a "coach week" button. I don't want each week to take too long, since part of the appeal is going through the season quickly.

So I would probably stray away from individual play calls for now, in favor of the playbooks and play styles we've talked about.

destilla commented 8 years ago

Looking like my initial estimate of having this done today was a little optimistic; I under estimated the complexity of potential situations and how busy I would be at work today. Still making good progress on it though

jonesguy14 commented 8 years ago

No worries, its a fairly big change and making sure that it operates well is important, given that the game simulation drives everything else. I wanted to implement other team histories this weekend but that might not happen due to a project I have to finish up. Maybe by next weekend we can put out an update