Validate options and apply defaults for options not supplied.

    :param opts: dictionary mapping str->str.
    :return: dictionary mapping str->Opt. All possible keys are present.

def get_opts(opts):
    """
    Validate options and apply defaults for options not supplied.

    :param opts: dictionary mapping str->str.
    :return: dictionary mapping str->Opt. All possible keys are present.
    """
    defaults = {
        'board': None,
        'terrain': Opt.random,
        'numbers': Opt.preset,
        'ports': Opt.preset,
        'pieces': Opt.preset,
        'players': Opt.preset,
    }
    _opts = defaults.copy()
    if opts is None:
        opts = dict()
    try:
        for key, val in opts.copy().items():
            if key == 'board':
                # board is a string, not a regular opt, and gets special handling
                # in _read_tiles_from_string
                continue
            opts[key] = Opt(val)
        _opts.update(opts)
    except Exception:
        raise ValueError('Invalid options={}'.format(opts))
    logging.debug('used defaults=\n{}\n on opts=\n{}\nreturned total opts=\n{}'.format(
        pprint.pformat(defaults),
        pprint.pformat(opts),
        pprint.pformat(_opts)))
    return _opts