rename coach to insights
parent
9cec8a3638
commit
9dc9eeb5fd
|
@ -136,5 +136,5 @@ object Env {
|
|||
def video = lila.video.Env.current
|
||||
def playban = lila.playban.Env.current
|
||||
def shutup = lila.shutup.Env.current
|
||||
def coach = lila.coach.Env.current
|
||||
def insight = lila.insight.Env.current
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import play.api.libs.json.Json
|
|||
import play.api.mvc._
|
||||
import views._
|
||||
|
||||
object Coach extends LilaController {
|
||||
object Insight extends LilaController {
|
||||
|
||||
private def env = Env.coach
|
||||
private def env = Env.insight
|
||||
|
||||
def refresh(username: String) = Open { implicit ctx =>
|
||||
Accessible(username) { user =>
|
||||
|
@ -18,23 +18,23 @@ object Coach extends LilaController {
|
|||
|
||||
def index(username: String) = Open { implicit ctx =>
|
||||
Accessible(username) { user =>
|
||||
import lila.coach.CoachApi.UserStatus._
|
||||
import lila.insight.InsightApi.UserStatus._
|
||||
env.api.userStatus(user).map {
|
||||
case NoGame => Ok(html.coach.noGame(user))
|
||||
case Empty => Ok(html.coach.empty(user))
|
||||
case s => Ok(html.coach.index(user, env.jsonView.stringifiedUi, s == Stale))
|
||||
case NoGame => Ok(html.insight.noGame(user))
|
||||
case Empty => Ok(html.insight.empty(user))
|
||||
case s => Ok(html.insight.index(user, env.jsonView.stringifiedUi, s == Stale))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def json(username: String) = OpenBody(BodyParsers.parse.json) { implicit ctx =>
|
||||
import lila.coach.JsonQuestion, JsonQuestion._
|
||||
import lila.insight.JsonQuestion, JsonQuestion._
|
||||
Accessible(username) { user =>
|
||||
ctx.body.body.validate[JsonQuestion].fold(
|
||||
err => BadRequest(Json.obj("error" -> err.toString)).fuccess,
|
||||
qJson => qJson.question.fold(BadRequest.fuccess) { q =>
|
||||
env.api.ask(q, user) map
|
||||
lila.coach.Chart.fromAnswer map
|
||||
lila.insight.Chart.fromAnswer map
|
||||
env.jsonView.chart.apply map { Ok(_) }
|
||||
}
|
||||
)
|
||||
|
@ -47,7 +47,7 @@ object Coach extends LilaController {
|
|||
case Some(u) => env.share.grant(u, ctx.me) flatMap {
|
||||
case true => f(u)
|
||||
case false if isGranted(_.UserSpy) => f(u)
|
||||
case false => fuccess(Forbidden(html.coach.forbidden(u)))
|
||||
case false => fuccess(Forbidden(html.insight.forbidden(u)))
|
||||
}
|
||||
}
|
||||
|
|
@ -101,8 +101,8 @@
|
|||
@base.radios(form("message"), translatedMessageChoices)
|
||||
</li>
|
||||
<li>
|
||||
<h2>Share your coach data</h2>
|
||||
@base.radios(form("coachShare"), lila.pref.Pref.CoachShare.choices)
|
||||
<h2>Share your insights data</h2>
|
||||
@base.radios(form("insightShare"), lila.pref.Pref.InsightShare.choices)
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
@(answer: lila.coach.Answer[_], u: User)(implicit ctx: Context)
|
||||
|
||||
@coach.layout(u, title = s"${u.username} coach") {
|
||||
|
||||
@answer
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@coach.layout(u,
|
||||
title = s"${u.username} coach",
|
||||
moreJs = jsTag("coach-refresh.js")) {
|
||||
|
||||
<div class="content_box small_box">
|
||||
<div class="head">
|
||||
<h1 class="text" data-icon="^">@u.username's coach</h1>
|
||||
</div>
|
||||
<br /><br /><br />
|
||||
<p>@userLink(u) has not used the coach yet!</p>
|
||||
<br /><br />
|
||||
@refreshForm(u, s"Start ${u.username} coach")
|
||||
</div>
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@coach.layout(u, title = s"${u.username} coach data is protected") {
|
||||
|
||||
You cannot see @userLink(u) coach data!
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@coach.layout(u, title = s"${u.username} has not played a rated game yet!") {
|
||||
|
||||
Before using the coach, @userLink(u) has to play at least one rated game.
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@insight.layout(u,
|
||||
title = s"${u.username} chess insights",
|
||||
moreJs = jsTag("insight-refresh.js")) {
|
||||
|
||||
<div class="content_box small_box">
|
||||
<div class="head">
|
||||
<h1 class="text" data-icon="^">@u.username chess insights</h1>
|
||||
</div>
|
||||
<br /><br /><br />
|
||||
<p>@userLink(u) has no chess insights yet!</p>
|
||||
<br /><br />
|
||||
@refreshForm(u, s"Generate ${u.username} chess insights")
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@insight.layout(u, title = s"${u.username} chess insights are protected") {
|
||||
|
||||
You cannot see @userLink(u) chess insights!
|
||||
}
|
|
@ -3,13 +3,13 @@
|
|||
@moreJs = {
|
||||
@highchartsLatestTag
|
||||
@jsAt("vendor/multiple-select/multiple-select.js")
|
||||
@jsAt(s"compiled/lichess.coach${isProd??(".min")}.js")
|
||||
@jsAt(s"compiled/lichess.insight${isProd??(".min")}.js")
|
||||
@if(stale) {
|
||||
@jsTag("coach-refresh.js")
|
||||
@jsTag("insight-refresh.js")
|
||||
}
|
||||
@embedJs {
|
||||
$(function() {
|
||||
LichessCoach(document.getElementById('coach'), {
|
||||
LichessInsight(document.getElementById('insight'), {
|
||||
ui: @Html(ui),
|
||||
i18n: @jsI18n(),
|
||||
userId: "@u.id"
|
||||
|
@ -19,19 +19,19 @@ userId: "@u.id"
|
|||
}
|
||||
|
||||
@moreCss = {
|
||||
@cssTag("coach.css")
|
||||
@cssTag("insight.css")
|
||||
@cssVendorTag("multiple-select/multiple-select.css")
|
||||
}
|
||||
|
||||
@coach.layout(u,
|
||||
title = s"${u.username} coach",
|
||||
@insight.layout(u,
|
||||
title = s"${u.username} chess insights",
|
||||
moreJs = moreJs,
|
||||
moreCss = moreCss) {
|
||||
<div id="coach"></div>
|
||||
<div id="insight"></div>
|
||||
@if(stale) {
|
||||
<div class="coach-stale none">
|
||||
<p>There are new games to analyse!</p>
|
||||
@refreshForm(u, "Update the coach")
|
||||
<div class="insight-stale none">
|
||||
<p>There are new games to learn from!</p>
|
||||
@refreshForm(u, "Update insights")
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@(u: User)(implicit ctx: Context)
|
||||
|
||||
@insight.layout(u, title = s"${u.username} has not played a rated game yet!") {
|
||||
|
||||
Before using chess insights, @userLink(u) has to play at least one rated game.
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
@(u: User, action: String)(implicit ctx: Context)
|
||||
|
||||
<form class="coach-refresh" method="post" action="@routes.Coach.refresh(u.username)">
|
||||
<form class="insight-refresh" method="post" action="@routes.Insight.refresh(u.username)">
|
||||
<button data-icon="E" class="button text">@action</button>
|
||||
<div class="crunshing none">
|
||||
<span class="square-spin"></span> Now crunshing data just for you!
|
|
@ -547,13 +547,13 @@ importer {
|
|||
delay = 50 milliseconds
|
||||
}
|
||||
mobile.app.version = "1.3.0"
|
||||
coach {
|
||||
insight {
|
||||
mongodb {
|
||||
uri = "mongodb://127.0.0.1:27037/lichess"
|
||||
mongo-async-driver = {}
|
||||
}
|
||||
collection {
|
||||
entry = coach_entry
|
||||
entry = insight
|
||||
}
|
||||
}
|
||||
simulation {
|
||||
|
|
|
@ -42,10 +42,10 @@ GET /@/:username/followers controllers.Relation.followers(username:
|
|||
GET /@/:username/suggestions controllers.Relation.suggest(username: String)
|
||||
GET /rel/blocks controllers.Relation.blocks
|
||||
|
||||
# Coach
|
||||
POST /coach/refresh/:username controllers.Coach.refresh(username: String)
|
||||
GET /coach/:username controllers.Coach.index(username: String)
|
||||
POST /coach/data/:username controllers.Coach.json(username: String)
|
||||
# Insight
|
||||
POST /insights/refresh/:username controllers.Insight.refresh(username: String)
|
||||
POST /insights/data/:username controllers.Insight.json(username: String)
|
||||
GET /@/:username/insights controllers.Insight.index(username: String)
|
||||
|
||||
# User
|
||||
GET /@/:username/opponents controllers.User.opponents(username: String)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import lila.pref.Pref
|
||||
import lila.user.User
|
||||
|
||||
final class Share(
|
||||
getPref: String => Fu[Pref],
|
||||
areFriends: (String, String) => Fu[Boolean]) {
|
||||
|
||||
def grant(coached: User, to: Option[User]): Fu[Boolean] = getPref(coached.id) flatMap { pref =>
|
||||
pref.coachShare match {
|
||||
case _ if to.contains(coached) => fuccess(true)
|
||||
case Pref.CoachShare.EVERYBODY => fuccess(true)
|
||||
case Pref.CoachShare.FRIENDS => to ?? { t => areFriends(coached.id, t.id) }
|
||||
case Pref.CoachShare.NOBODY => fuccess(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package lila
|
||||
|
||||
package object coach extends PackageObject with WithPlay
|
|
@ -1,95 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import akka.actor._
|
||||
import akka.pattern.ask
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
||||
import play.api.libs.json.Json
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.db.api._
|
||||
import lila.db.BSON._
|
||||
import lila.db.Implicits._
|
||||
import lila.game.BSONHandlers.gameBSONHandler
|
||||
import lila.game.tube.gameTube
|
||||
import lila.game.{ Game, Query }
|
||||
import lila.hub.Sequencer
|
||||
import lila.user.User
|
||||
|
||||
final class Aggregator(api: StatApi, sequencer: ActorRef) {
|
||||
|
||||
private implicit val timeout = makeTimeout.minutes(5)
|
||||
|
||||
def apply(user: User): Funit = {
|
||||
val p = scala.concurrent.Promise[Unit]()
|
||||
sequencer ! Sequencer.work(compute(user), p.some)
|
||||
p.future
|
||||
}
|
||||
|
||||
private def compute(user: User): Funit = api.fetchLast(user) flatMap {
|
||||
case None => fromScratch(user)
|
||||
case Some(p) => api.remove(p) >> computeFrom(user, p.from)
|
||||
}
|
||||
|
||||
private def fromScratch(user: User): Funit =
|
||||
fetchFirstGame(user) flatMap {
|
||||
_.?? { g => computeFrom(user, g.createdAt) }
|
||||
}
|
||||
|
||||
private def gameQuery(user: User) = Query.user(user.id) ++ Query.rated ++ Query.finished
|
||||
private val maxGames = 5 * 1000
|
||||
|
||||
private def fetchFirstGame(user: User): Fu[Option[Game]] =
|
||||
if (user.count.rated == 0) fuccess(none)
|
||||
else {
|
||||
(user.count.rated >= maxGames) ??
|
||||
pimpQB($query(gameQuery(user))).sort(Query.sortCreated).skip(maxGames - 1).one[Game]
|
||||
} orElse
|
||||
pimpQB($query(gameQuery(user))).sort(Query.sortChronological).one[Game]
|
||||
|
||||
private def computeFrom(user: User, from: DateTime): Funit =
|
||||
lila.common.Chronometer.log(s"aggregator:${user.username}") {
|
||||
{
|
||||
pimpQB($query(gameQuery(user) ++ Json.obj(Game.BSONFields.createdAt -> $gte($date(from)))))
|
||||
.sort(Query.sortChronological)
|
||||
.cursor[Game]()
|
||||
.enumerate(maxGames, stopOnError = true) &>
|
||||
richPovEnumeratee(user) |>>>
|
||||
Iteratee.foldM[Option[RichPov], Periods.Computation](Periods.initComputation(user.id, api.insert)) {
|
||||
case (comp, Some(p)) => try {
|
||||
comp aggregate p
|
||||
}
|
||||
catch {
|
||||
case e: Exception =>
|
||||
e.printStackTrace
|
||||
logwarn(s"[StatApi] game ${p.pov.game.id} $e"); fuccess(comp)
|
||||
}
|
||||
// case (comp, Some(p)) => comp aggregate p
|
||||
case (comp, _) => logwarn("[StatApi] invalid pov"); fuccess(comp)
|
||||
}
|
||||
}.map(_.run)
|
||||
}
|
||||
|
||||
private def richPovEnumeratee(user: User) =
|
||||
Enumeratee.mapM[lila.game.Game].apply[Option[RichPov]] { game =>
|
||||
lila.game.Pov.ofUserId(game, user.id) ?? { pov =>
|
||||
lila.game.GameRepo.initialFen(game) zip
|
||||
(game.metadata.analysed ?? lila.analyse.AnalysisRepo.doneById(game.id)) map {
|
||||
case (fen, an) =>
|
||||
val division = chess.Replay.boards(
|
||||
moveStrs = game.pgnMoves,
|
||||
initialFen = fen,
|
||||
variant = game.variant
|
||||
).toOption.fold(chess.Division.empty)(chess.Divider.apply)
|
||||
RichPov(
|
||||
pov = pov,
|
||||
initialFen = fen,
|
||||
analysis = an,
|
||||
division = division,
|
||||
accuracy = an.flatMap { lila.analyse.Accuracy(pov, _, division) },
|
||||
moveAccuracy = an.map { lila.analyse.Accuracy.diffsList(pov, _) }
|
||||
).some
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import reactivemongo.bson._
|
||||
import reactivemongo.bson.Macros
|
||||
|
||||
import lila.db.BSON._
|
||||
import lila.db.Implicits._
|
||||
import lila.rating.PerfType
|
||||
|
||||
private[coach] object BSONHandlers {
|
||||
|
||||
import Results.{ BestWin }
|
||||
import PerfResults.{ BestRating, StatusScores, OutcomeStatuses, PerfResultsMap }
|
||||
import Openings.OpeningsMap
|
||||
import GameSections.Section
|
||||
|
||||
private implicit val intMapHandler = MapValue.MapHandler[Int]
|
||||
|
||||
private implicit val NbSumBSONHandler = new BSONHandler[BSONArray, NbSum] {
|
||||
def read(arr: BSONArray) = NbSum(
|
||||
nb = arr.getAs[Int](0) err "NbSum missing nb",
|
||||
sum = arr.getAs[Int](1) err "NbSum missing sum")
|
||||
def write(x: NbSum) = BSONArray(x.nb, x.sum)
|
||||
}
|
||||
private implicit val StatusScoresBSONHandler = new BSONHandler[BSONDocument, StatusScores] {
|
||||
def read(doc: BSONDocument): StatusScores = StatusScores {
|
||||
intMapHandler read doc mapKeys { k =>
|
||||
parseIntOption(k) flatMap chess.Status.apply
|
||||
} collect { case (Some(k), v) => k -> v }
|
||||
}
|
||||
def write(x: StatusScores) = intMapHandler write x.m.mapKeys(_.id.toString)
|
||||
}
|
||||
implicit val MoveBSONHandler = Macros.handler[Move]
|
||||
implicit val TrimmedMovesBSONHandler = new BSONHandler[BSONArray, TrimmedMoves] {
|
||||
def read(a: BSONArray) = TrimmedMoves {
|
||||
a.values.collect {
|
||||
case BSONInteger(i) => i
|
||||
}.grouped(5).foldLeft(Vector.empty[Move]) {
|
||||
case (acc, i) =>
|
||||
acc :+ Move(i(0), NbSum(i(1), i(2)), NbSum(i(3), i(4)))
|
||||
}
|
||||
}
|
||||
def write(x: TrimmedMoves) = BSONArray {
|
||||
x.moves.toStream.flatMap { m =>
|
||||
List(m.nb, m.acpl.nb, m.acpl.sum, m.time.nb, m.time.sum)
|
||||
} map BSONInteger.apply
|
||||
}
|
||||
}
|
||||
implicit val ColorMovesBSONHandler = Macros.handler[ColorMoves]
|
||||
|
||||
implicit val PerfResultsOutcomeStatusesBSONHandler = Macros.handler[OutcomeStatuses]
|
||||
implicit val ResultsBestWinBSONHandler = Macros.handler[BestWin]
|
||||
implicit val PerfResultsBestRatingBSONHandler = Macros.handler[BestRating]
|
||||
implicit val SectionBSONHandler = Macros.handler[Section]
|
||||
implicit val GameSectionsBSONHandler = Macros.handler[GameSections]
|
||||
implicit val ResultsBSONHandler = Macros.handler[Results]
|
||||
implicit val PerfResultsBSONHandler = Macros.handler[PerfResults]
|
||||
|
||||
private val perfResultsMapHandler = Map.MapHandler[PerfResults]
|
||||
private implicit val PerfResultsMapBSONHandler = new BSONHandler[BSONDocument, PerfResultsMap] {
|
||||
def read(doc: BSONDocument): PerfResultsMap = PerfResultsMap {
|
||||
perfResultsMapHandler read doc mapKeys PerfType.apply collect { case (Some(k), v) => k -> v }
|
||||
}
|
||||
def write(x: PerfResultsMap) = perfResultsMapHandler write x.m.mapKeys(_.key)
|
||||
}
|
||||
|
||||
private val resultsMapHandler = Map.MapHandler[Results]
|
||||
private implicit val OpeningsMapBSONHandler = new BSONHandler[BSONDocument, OpeningsMap] {
|
||||
def read(doc: BSONDocument): OpeningsMap = OpeningsMap(resultsMapHandler read doc)
|
||||
def write(x: OpeningsMap) = resultsMapHandler write x.m
|
||||
}
|
||||
implicit val OpeningsBSONHandler = Macros.handler[Openings]
|
||||
implicit val ColorResultsBSONHandler = Macros.handler[ColorResults]
|
||||
|
||||
implicit val UserStatBSONHandler = new lila.db.BSON[UserStat] {
|
||||
def reads(r: lila.db.BSON.Reader) = {
|
||||
UserStat(
|
||||
colorResults = r.getO[ColorResults]("colorResults") | ColorResults.empty,
|
||||
openings = r.getO[Openings]("openings") | Openings.empty,
|
||||
results = r.getO[PerfResults]("results") | PerfResults.empty,
|
||||
perfResults = r.getO[PerfResults.PerfResultsMap]("perfResults") | PerfResults.emptyPerfResultsMap
|
||||
)
|
||||
}
|
||||
def writes(w: lila.db.BSON.Writer, o: UserStat) = BSONDocument(
|
||||
"colorResults" -> o.colorResults,
|
||||
"openings" -> o.openings,
|
||||
"results" -> o.results,
|
||||
"perfResults" -> o.perfResults)
|
||||
}
|
||||
implicit val PeriodBSONHandler = Macros.handler[Period]
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
case class ColorResults(white: Results, black: Results) {
|
||||
|
||||
def apply(c: chess.Color) = c.fold(white, black)
|
||||
|
||||
def merge(o: ColorResults) = ColorResults(
|
||||
white = white merge o.white,
|
||||
black = black merge o.black)
|
||||
}
|
||||
|
||||
object ColorResults {
|
||||
|
||||
val empty = ColorResults(Results.empty, Results.empty)
|
||||
|
||||
case class Computation(white: Results.Computation, black: Results.Computation) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
white = if (p.pov.color.white) white aggregate p else white,
|
||||
black = if (p.pov.color.black) black aggregate p else black)
|
||||
|
||||
def run = ColorResults(white = white.run, black = black.run)
|
||||
}
|
||||
val emptyComputation = Computation(Results.emptyComputation, Results.emptyComputation)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
case class Ecopening(
|
||||
eco: Ecopening.ECO,
|
||||
family: Ecopening.FamilyName,
|
||||
name: String,
|
||||
moves: String,
|
||||
fen: Ecopening.FEN,
|
||||
lastMoveUci: String) {
|
||||
|
||||
lazy val moveList = moves.split(' ').toList
|
||||
|
||||
def firstMove = moveList.headOption
|
||||
|
||||
lazy val size = moveList.size
|
||||
|
||||
lazy val formattedMoves: String =
|
||||
moveList.grouped(2).zipWithIndex.map {
|
||||
case (List(w, b), i) => s"${i + 1}. $w $b"
|
||||
case (List(w), i) => s"${i + 1}. $w"
|
||||
case _ => ""
|
||||
}.mkString(" ")
|
||||
|
||||
override def toString = s"$eco $name ($moves)"
|
||||
}
|
||||
|
||||
object Ecopening {
|
||||
type FamilyName = String
|
||||
type ECO = String
|
||||
type FEN = String
|
||||
|
||||
case class Family(name: FamilyName, ecos: List[FEN])
|
||||
def makeFamilies(ops: Iterable[Ecopening]): Map[FamilyName, Family] =
|
||||
ops.foldLeft(Map.empty[FamilyName, Family]) {
|
||||
case (fams, op) => fams + (op.family -> fams.get(op.family).fold(Family(op.family, List(op.eco))) {
|
||||
existing => existing.copy(ecos = op.eco :: existing.ecos)
|
||||
})
|
||||
}
|
||||
|
||||
def fromGame(game: lila.game.Game): Option[Ecopening] =
|
||||
if (game.playable || game.turns < 4 || game.fromPosition || game.variant.exotic) none
|
||||
else chess.Replay.boards(
|
||||
moveStrs = game.pgnMoves take EcopeningDB.MAX_MOVES,
|
||||
initialFen = none,
|
||||
variant = chess.variant.Standard
|
||||
).toOption flatMap matchChronoBoards
|
||||
|
||||
private def matchChronoBoards(boards: List[chess.Board]): Option[Ecopening] =
|
||||
boards.reverse.foldLeft(none[Ecopening]) {
|
||||
case (acc, board) => acc orElse {
|
||||
EcopeningDB.allByFen get chess.format.Forsyth.exportBoard(board)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,514 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
private[coach] object EcopeningDB {
|
||||
|
||||
import Ecopening._
|
||||
|
||||
val MAX_MOVES = 25
|
||||
|
||||
lazy val allByFen: Map[FEN, Ecopening] = allByEco.map {
|
||||
case (_, opening) => opening.fen -> opening
|
||||
}
|
||||
|
||||
lazy val allByEco: Map[ECO, Ecopening] = Map(
|
||||
"A00" -> Ecopening("A00", "Uncommon Opening", "Uncommon Opening", "g4, a3, h3, etc.", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR", ""),
|
||||
"A01" -> Ecopening("A01", "Nimzovich-Larsen Attack", "Nimzovich-Larsen Attack", "b3", "rnbqkbnr/pppppppp/8/8/8/1P6/P1PPPPPP/RNBQKBNR", "b2b3"),
|
||||
"A02" -> Ecopening("A02", "Bird's Opening", "Bird's Opening", "f4", "rnbqkbnr/pppppppp/8/8/5P2/8/PPPPP1PP/RNBQKBNR", "f2f4"),
|
||||
"A03" -> Ecopening("A03", "Bird's Opening", "Bird's Opening", "f4 d5", "rnbqkbnr/ppp1pppp/8/3p4/5P2/8/PPPPP1PP/RNBQKBNR", "d7d5"),
|
||||
"A04" -> Ecopening("A04", "Reti Opening", "Reti Opening", "Nf3", "rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R", "g1f3"),
|
||||
"A05" -> Ecopening("A05", "Reti Opening", "Reti Opening", "Nf3 Nf6", "rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R", "g8f6"),
|
||||
"A06" -> Ecopening("A06", "Reti Opening", "Reti Opening", "Nf3 d5", "rnbqkbnr/ppp1pppp/8/3p4/8/5N2/PPPPPPPP/RNBQKB1R", "d7d5"),
|
||||
"A07" -> Ecopening("A07", "King's Indian Attack", "King's Indian Attack", "Nf3 d5 g3", "rnbqkbnr/ppp1pppp/8/3p4/8/5NP1/PPPPPP1P/RNBQKB1R", "g2g3"),
|
||||
"A08" -> Ecopening("A08", "King's Indian Attack", "King's Indian Attack", "Nf3 d5 g3 c5 Bg2", "rnbqkbnr/pp2pppp/8/2pp4/8/5NP1/PPPPPPBP/RNBQK2R", "f1g2"),
|
||||
"A09" -> Ecopening("A09", "Reti Opening", "Reti Opening", "Nf3 d5 c4", "rnbqkbnr/ppp1pppp/8/3p4/2P5/5N2/PP1PPPPP/RNBQKB1R", "c2c4"),
|
||||
"A10" -> Ecopening("A10", "English", "English", "c4", "rnbqkbnr/pppppppp/8/8/2P5/8/PP1PPPPP/RNBQKBNR", "c2c4"),
|
||||
"A11" -> Ecopening("A11", "English", "English, Caro-Kann Defensive System", "c4 c6", "rnbqkbnr/pp1ppppp/2p5/8/2P5/8/PP1PPPPP/RNBQKBNR", "c7c6"),
|
||||
"A12" -> Ecopening("A12", "English", "English with b3", "c4 c6 Nf3 d5 b3", "rnbqkbnr/pp2pppp/2p5/3p4/2P5/1P3N2/P2PPPPP/RNBQKB1R", "b2b3"),
|
||||
"A13" -> Ecopening("A13", "English", "English", "c4 e6", "rnbqkbnr/pppp1ppp/4p3/8/2P5/8/PP1PPPPP/RNBQKBNR", "e7e6"),
|
||||
"A14" -> Ecopening("A14", "English", "English", "c4 e6 Nf3 d5 g3 Nf6 Bg2 Be7 O-O", "rnbqk2r/ppp1bppp/4pn2/3p4/2P5/5NP1/PP1PPPBP/RNBQ1RK1", "e1g1"),
|
||||
"A15" -> Ecopening("A15", "English", "English", "c4 Nf6", "rnbqkb1r/pppppppp/5n2/8/2P5/8/PP1PPPPP/RNBQKBNR", "g8f6"),
|
||||
"A16" -> Ecopening("A16", "English", "English", "c4 Nf6 Nc3", "rnbqkb1r/pppppppp/5n2/8/2P5/2N5/PP1PPPPP/R1BQKBNR", "b1c3"),
|
||||
"A17" -> Ecopening("A17", "English", "English", "c4 Nf6 Nc3 e6", "rnbqkb1r/pppp1ppp/4pn2/8/2P5/2N5/PP1PPPPP/R1BQKBNR", "e7e6"),
|
||||
"A18" -> Ecopening("A18", "English", "English, Mikenas-Carls", "c4 Nf6 Nc3 e6 e4", "rnbqkb1r/pppp1ppp/4pn2/8/2P1P3/2N5/PP1P1PPP/R1BQKBNR", "e2e4"),
|
||||
"A19" -> Ecopening("A19", "English", "English, Mikenas-Carls, Sicilian Variation", "c4 Nf6 Nc3 e6 e4 c5", "rnbqkb1r/pp1p1ppp/4pn2/2p5/2P1P3/2N5/PP1P1PPP/R1BQKBNR", "c7c5"),
|
||||
"A20" -> Ecopening("A20", "English", "English", "c4 e5", "rnbqkbnr/pppp1ppp/8/4p3/2P5/8/PP1PPPPP/RNBQKBNR", "e7e5"),
|
||||
"A21" -> Ecopening("A21", "English", "English", "c4 e5 Nc3", "rnbqkbnr/pppp1ppp/8/4p3/2P5/2N5/PP1PPPPP/R1BQKBNR", "b1c3"),
|
||||
"A22" -> Ecopening("A22", "English", "English", "c4 e5 Nc3 Nf6", "rnbqkb1r/pppp1ppp/5n2/4p3/2P5/2N5/PP1PPPPP/R1BQKBNR", "g8f6"),
|
||||
"A23" -> Ecopening("A23", "English", "English, Bremen System, Keres Variation", "c4 e5 Nc3 Nf6 g3 c6", "rnbqkb1r/pp1p1ppp/2p2n2/4p3/2P5/2N3P1/PP1PPP1P/R1BQKBNR", "c7c6"),
|
||||
"A24" -> Ecopening("A24", "English", "English, Bremen System with ...g6", "c4 e5 Nc3 Nf6 g3 g6", "rnbqkb1r/pppp1p1p/5np1/4p3/2P5/2N3P1/PP1PPP1P/R1BQKBNR", "g7g6"),
|
||||
"A25" -> Ecopening("A25", "English", "English", "c4 e5 Nc3 Nc6", "r1bqkbnr/pppp1ppp/2n5/4p3/2P5/2N5/PP1PPPPP/R1BQKBNR", "b8c6"),
|
||||
"A26" -> Ecopening("A26", "English", "English", "c4 e5 Nc3 Nc6 g3 g6 Bg2 Bg7 d3 d6", "r1bqk1nr/ppp2pbp/2np2p1/4p3/2P5/2NP2P1/PP2PPBP/R1BQK1NR", "d7d6"),
|
||||
"A27" -> Ecopening("A27", "English", "English, Three Knights System", "c4 e5 Nc3 Nc6 Nf3", "r1bqkbnr/pppp1ppp/2n5/4p3/2P5/2N2N2/PP1PPPPP/R1BQKB1R", "g1f3"),
|
||||
"A28" -> Ecopening("A28", "English", "English", "c4 e5 Nc3 Nc6 Nf3 Nf6", "r1bqkb1r/pppp1ppp/2n2n2/4p3/2P5/2N2N2/PP1PPPPP/R1BQKB1R", "g8f6"),
|
||||
"A29" -> Ecopening("A29", "English", "English, Four Knights, Kingside Fianchetto", "c4 e5 Nc3 Nc6 Nf3 Nf6 g3", "r1bqkb1r/pppp1ppp/2n2n2/4p3/2P5/2N2NP1/PP1PPP1P/R1BQKB1R", "g2g3"),
|
||||
"A30" -> Ecopening("A30", "English", "English, Symmetrical", "c4 c5", "rnbqkbnr/pp1ppppp/8/2p5/2P5/8/PP1PPPPP/RNBQKBNR", "c7c5"),
|
||||
"A31" -> Ecopening("A31", "English", "English, Symmetrical, Benoni Formation", "c4 c5 Nf3 Nf6 d4", "rnbqkb1r/pp1ppppp/5n2/2p5/2PP4/5N2/PP2PPPP/RNBQKB1R", "d2d4"),
|
||||
"A32" -> Ecopening("A32", "English", "English, Symmetrical Variation", "c4 c5 Nf3 Nf6 d4 cxd4 Nxd4 e6", "rnbqkb1r/pp1p1ppp/4pn2/8/2PN4/8/PP2PPPP/RNBQKB1R", "e7e6"),
|
||||
"A33" -> Ecopening("A33", "English", "English, Symmetrical", "c4 c5 Nf3 Nf6 d4 cxd4 Nxd4 e6 Nc3 Nc6", "r1bqkb1r/pp1p1ppp/2n1pn2/8/2PN4/2N5/PP2PPPP/R1BQKB1R", "b8c6"),
|
||||
"A34" -> Ecopening("A34", "English", "English, Symmetrical", "c4 c5 Nc3", "rnbqkbnr/pp1ppppp/8/2p5/2P5/2N5/PP1PPPPP/R1BQKBNR", "b1c3"),
|
||||
"A35" -> Ecopening("A35", "English", "English, Symmetrical", "c4 c5 Nc3 Nc6", "r1bqkbnr/pp1ppppp/2n5/2p5/2P5/2N5/PP1PPPPP/R1BQKBNR", "b8c6"),
|
||||
"A36" -> Ecopening("A36", "English", "English", "c4 c5 Nc3 Nc6 g3", "r1bqkbnr/pp1ppppp/2n5/2p5/2P5/2N3P1/PP1PPP1P/R1BQKBNR", "g2g3"),
|
||||
"A37" -> Ecopening("A37", "English", "English, Symmetrical", "c4 c5 Nc3 Nc6 g3 g6 Bg2 Bg7 Nf3", "r1bqk1nr/pp1pppbp/2n3p1/2p5/2P5/2N2NP1/PP1PPPBP/R1BQK2R", "g1f3"),
|
||||
"A38" -> Ecopening("A38", "English", "English, Symmetrical", "c4 c5 Nc3 Nc6 g3 g6 Bg2 Bg7 Nf3 Nf6", "r1bqk2r/pp1pppbp/2n2np1/2p5/2P5/2N2NP1/PP1PPPBP/R1BQK2R", "g8f6"),
|
||||
"A39" -> Ecopening("A39", "English", "English, Symmetrical, Main line with d4", "c4 c5 Nc3 Nc6 g3 g6 Bg2 Bg7 Nf3 Nf6 O-O O-O d4", "r1bq1rk1/pp1pppbp/2n2np1/2p5/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "d2d4"),
|
||||
"A40" -> Ecopening("A40", "Queen's Pawn Game", "Queen's Pawn Game", "d4", "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR", "d2d4"),
|
||||
"A41" -> Ecopening("A41", "Queen's Pawn Game", "Queen's Pawn Game (with ...d6)", "d4 d6", "rnbqkbnr/ppp1pppp/3p4/8/3P4/8/PPP1PPPP/RNBQKBNR", "d7d6"),
|
||||
"A42" -> Ecopening("A42", "Modern Defence", "Modern Defence, Averbakh System", "d4 d6 c4 g6 Nc3 Bg7 e4", "rnbqk1nr/ppp1ppbp/3p2p1/8/2PPP3/2N5/PP3PPP/R1BQKBNR", "e2e4"),
|
||||
"A43" -> Ecopening("A43", "Old Benoni", "Old Benoni", "d4 c5", "rnbqkbnr/pp1ppppp/8/2p5/3P4/8/PPP1PPPP/RNBQKBNR", "c7c5"),
|
||||
"A44" -> Ecopening("A44", "Old Benoni", "Old Benoni Defence", "d4 c5 d5 e5", "rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PPP1PPPP/RNBQKBNR", "e7e5"),
|
||||
"A45" -> Ecopening("A45", "Queen's Pawn Game", "Queen's Pawn Game", "d4 Nf6", "rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR", "g8f6"),
|
||||
"A46" -> Ecopening("A46", "Queen's Pawn Game", "Queen's Pawn Game", "d4 Nf6 Nf3", "rnbqkb1r/pppppppp/5n2/8/3P4/5N2/PPP1PPPP/RNBQKB1R", "g1f3"),
|
||||
"A47" -> Ecopening("A47", "Queen's Indian", "Queen's Indian", "d4 Nf6 Nf3 b6", "rnbqkb1r/p1pppppp/1p3n2/8/3P4/5N2/PPP1PPPP/RNBQKB1R", "b7b6"),
|
||||
"A48" -> Ecopening("A48", "King's Indian", "King's Indian", "d4 Nf6 Nf3 g6", "rnbqkb1r/pppppp1p/5np1/8/3P4/5N2/PPP1PPPP/RNBQKB1R", "g7g6"),
|
||||
"A49" -> Ecopening("A49", "King's Indian", "King's Indian, Fianchetto without c4", "d4 Nf6 Nf3 g6 g3", "rnbqkb1r/pppppp1p/5np1/8/3P4/5NP1/PPP1PP1P/RNBQKB1R", "g2g3"),
|
||||
"A50" -> Ecopening("A50", "Queen's Pawn Game", "Queen's Pawn Game", "d4 Nf6 c4", "rnbqkb1r/pppppppp/5n2/8/2PP4/8/PP2PPPP/RNBQKBNR", "c2c4"),
|
||||
"A51" -> Ecopening("A51", "Budapest Gambit", "Budapest Gambit", "d4 Nf6 c4 e5", "rnbqkb1r/pppp1ppp/5n2/4p3/2PP4/8/PP2PPPP/RNBQKBNR", "e7e5"),
|
||||
"A52" -> Ecopening("A52", "Budapest Gambit", "Budapest Gambit", "d4 Nf6 c4 e5 dxe5 Ng4", "rnbqkb1r/pppp1ppp/8/4P3/2P3n1/8/PP2PPPP/RNBQKBNR", "f6g4"),
|
||||
"A53" -> Ecopening("A53", "Old Indian", "Old Indian", "d4 Nf6 c4 d6", "rnbqkb1r/ppp1pppp/3p1n2/8/2PP4/8/PP2PPPP/RNBQKBNR", "d7d6"),
|
||||
"A54" -> Ecopening("A54", "Old Indian", "Old Indian, Ukrainian Variation, 4.Nf3", "d4 Nf6 c4 d6 Nc3 e5 Nf3", "rnbqkb1r/ppp2ppp/3p1n2/4p3/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "g1f3"),
|
||||
"A55" -> Ecopening("A55", "Old Indian", "Old Indian, Main line", "d4 Nf6 c4 d6 Nc3 e5 Nf3 Nbd7 e4", "r1bqkb1r/pppn1ppp/3p1n2/4p3/2PPP3/2N2N2/PP3PPP/R1BQKB1R", "e2e4"),
|
||||
"A56" -> Ecopening("A56", "Benoni", "Benoni Defence", "d4 Nf6 c4 c5", "rnbqkb1r/pp1ppppp/5n2/2p5/2PP4/8/PP2PPPP/RNBQKBNR", "c7c5"),
|
||||
"A57" -> Ecopening("A57", "Benko Gambit", "Benko Gambit", "d4 Nf6 c4 c5 d5 b5", "rnbqkb1r/p2ppppp/5n2/1ppP4/2P5/8/PP2PPPP/RNBQKBNR", "b7b5"),
|
||||
"A58" -> Ecopening("A58", "Benko Gambit", "Benko Gambit", "d4 Nf6 c4 c5 d5 b5 cxb5 a6 bxa6", "rnbqkb1r/3ppppp/P4n2/2pP4/8/8/PP2PPPP/RNBQKBNR", "b5a6"),
|
||||
"A59" -> Ecopening("A59", "Benko Gambit", "Benko Gambit", "d4 Nf6 c4 c5 d5 b5 cxb5 a6 bxa6 Bxa6 Nc3 d6 e4", "rn1qkb1r/4pppp/b2p1n2/2pP4/4P3/2N5/PP3PPP/R1BQKBNR", "e2e4"),
|
||||
"A60" -> Ecopening("A60", "Benoni", "Benoni Defence", "d4 Nf6 c4 c5 d5 e6", "rnbqkb1r/pp1p1ppp/4pn2/2pP4/2P5/8/PP2PPPP/RNBQKBNR", "e7e6"),
|
||||
"A61" -> Ecopening("A61", "Benoni", "Benoni", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 Nf3 g6", "rnbqkb1r/pp3p1p/3p1np1/2pP4/8/2N2N2/PP2PPPP/R1BQKB1R", "g7g6"),
|
||||
"A62" -> Ecopening("A62", "Benoni", "Benoni, Fianchetto Variation", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 Nf3 g6 g3 Bg7 Bg2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/8/2N2NP1/PP2PPBP/R1BQK2R", "e8g8"),
|
||||
"A63" -> Ecopening("A63", "Benoni", "Benoni, Fianchetto, 9...Nbd7", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 Nf3 g6 g3 Bg7 Bg2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/8/2N2NP1/PP2PPBP/R1BQK2R", "e8g8"),
|
||||
"A64" -> Ecopening("A64", "Benoni", "Benoni, Fianchetto, 11...Re8", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 Nf3 g6 g3 Bg7 Bg2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/8/2N2NP1/PP2PPBP/R1BQK2R", "e8g8"),
|
||||
"A65" -> Ecopening("A65", "Benoni", "Benoni, 6.e4", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4", "rnbqkb1r/pp3ppp/3p1n2/2pP4/4P3/2N5/PP3PPP/R1BQKBNR", "e2e4"),
|
||||
"A66" -> Ecopening("A66", "Benoni", "Benoni", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 f4", "rnbqkb1r/pp3p1p/3p1np1/2pP4/4PP2/2N5/PP4PP/R1BQKBNR", "f2f4"),
|
||||
"A67" -> Ecopening("A67", "Benoni", "Benoni, Taimanov Variation", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 f4 Bg7 Bb5+", "rnbqk2r/pp3pbp/3p1np1/1BpP4/4PP2/2N5/PP4PP/R1BQK1NR", "f1b5"),
|
||||
"A68" -> Ecopening("A68", "Benoni", "Benoni, Four Pawns Attack", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 f4 Bg7 Nf3 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4PP2/2N2N2/PP4PP/R1BQKB1R", "e8g8"),
|
||||
"A69" -> Ecopening("A69", "Benoni", "Benoni, Four Pawns Attack, Main line", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 f4 Bg7 Nf3 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4PP2/2N2N2/PP4PP/R1BQKB1R", "e8g8"),
|
||||
"A70" -> Ecopening("A70", "Benoni", "Benoni, Classical with 7.Nf3", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3", "rnbqkb1r/pp3p1p/3p1np1/2pP4/4P3/2N2N2/PP3PPP/R1BQKB1R", "g1f3"),
|
||||
"A71" -> Ecopening("A71", "Benoni", "Benoni, Classical, 8.Bg5", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Bg5", "rnbqk2r/pp3pbp/3p1np1/2pP2B1/4P3/2N2N2/PP3PPP/R2QKB1R", "c1g5"),
|
||||
"A72" -> Ecopening("A72", "Benoni", "Benoni, Classical without 9.O-O", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A73" -> Ecopening("A73", "Benoni", "Benoni, Classical, 9.O-O", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A74" -> Ecopening("A74", "Benoni", "Benoni, Classical, 9...a6, 10.a4", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A75" -> Ecopening("A75", "Benoni", "Benoni, Classical with ...a6 and 10...Bg4", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A76" -> Ecopening("A76", "Benoni", "Benoni, Classical, 9...Re8", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A77" -> Ecopening("A77", "Benoni", "Benoni, Classical, 9...Re8, 10.Nd2", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A78" -> Ecopening("A78", "Benoni", "Benoni, Classical with ...Re8 and ...Na6", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A79" -> Ecopening("A79", "Benoni", "Benoni, Classical, 11.f3", "d4 Nf6 c4 c5 d5 e6 Nc3 exd5 cxd5 d6 e4 g6 Nf3 Bg7 Be2 O-O", "rnbq1rk1/pp3pbp/3p1np1/2pP4/4P3/2N2N2/PP2BPPP/R1BQK2R", "e8g8"),
|
||||
"A80" -> Ecopening("A80", "Dutch", "Dutch", "d4 f5", "rnbqkbnr/ppppp1pp/8/5p2/3P4/8/PPP1PPPP/RNBQKBNR", "f7f5"),
|
||||
"A81" -> Ecopening("A81", "Dutch", "Dutch", "d4 f5 g3", "rnbqkbnr/ppppp1pp/8/5p2/3P4/6P1/PPP1PP1P/RNBQKBNR", "g2g3"),
|
||||
"A82" -> Ecopening("A82", "Dutch", "Dutch, Staunton Gambit", "d4 f5 e4", "rnbqkbnr/ppppp1pp/8/5p2/3PP3/8/PPP2PPP/RNBQKBNR", "e2e4"),
|
||||
"A83" -> Ecopening("A83", "Dutch", "Dutch, Staunton Gambit", "d4 f5 e4 fxe4 Nc3 Nf6 Bg5", "rnbqkb1r/ppppp1pp/5n2/6B1/3Pp3/2N5/PPP2PPP/R2QKBNR", "c1g5"),
|
||||
"A84" -> Ecopening("A84", "Dutch", "Dutch", "d4 f5 c4", "rnbqkbnr/ppppp1pp/8/5p2/2PP4/8/PP2PPPP/RNBQKBNR", "c2c4"),
|
||||
"A85" -> Ecopening("A85", "Dutch", "Dutch, with c4 & Nc3", "d4 f5 c4 Nf6 Nc3", "rnbqkb1r/ppppp1pp/5n2/5p2/2PP4/2N5/PP2PPPP/R1BQKBNR", "b1c3"),
|
||||
"A86" -> Ecopening("A86", "Dutch", "Dutch", "d4 f5 c4 Nf6 g3", "rnbqkb1r/ppppp1pp/5n2/5p2/2PP4/6P1/PP2PP1P/RNBQKBNR", "g2g3"),
|
||||
"A87" -> Ecopening("A87", "Dutch", "Dutch, Leningrad, Main Variation", "d4 f5 c4 Nf6 g3 g6 Bg2 Bg7 Nf3", "rnbqk2r/ppppp1bp/5np1/5p2/2PP4/5NP1/PP2PPBP/RNBQK2R", "g1f3"),
|
||||
"A88" -> Ecopening("A88", "Dutch", "Dutch, Leningrad, Main Variation with c6", "d4 f5 c4 Nf6 g3 g6 Bg2 Bg7 Nf3 O-O O-O d6 Nc3 c6", "rnbq1rk1/pp2p1bp/2pp1np1/5p2/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "c7c6"),
|
||||
"A89" -> Ecopening("A89", "Dutch", "Dutch, Leningrad, Main Variation with Nc6", "d4 f5 c4 Nf6 g3 g6 Bg2 Bg7 Nf3 O-O O-O d6 Nc3 Nc6", "r1bq1rk1/ppp1p1bp/2np1np1/5p2/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "b8c6"),
|
||||
"A90" -> Ecopening("A90", "Dutch", "Dutch", "d4 f5 c4 Nf6 g3 e6 Bg2", "rnbqkb1r/pppp2pp/4pn2/5p2/2PP4/6P1/PP2PPBP/RNBQK1NR", "f1g2"),
|
||||
"A91" -> Ecopening("A91", "Dutch", "Dutch Defence", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7", "rnbqk2r/ppppb1pp/4pn2/5p2/2PP4/6P1/PP2PPBP/RNBQK1NR", "f8e7"),
|
||||
"A92" -> Ecopening("A92", "Dutch", "Dutch", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O", "rnbq1rk1/ppppb1pp/4pn2/5p2/2PP4/5NP1/PP2PPBP/RNBQK2R", "e8g8"),
|
||||
"A93" -> Ecopening("A93", "Dutch", "Dutch, Stonewall, Botvinnik Variation", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d5 b3", "rnbq1rk1/ppp1b1pp/4pn2/3p1p2/2PP4/1P3NP1/P3PPBP/RNBQ1RK1", "b2b3"),
|
||||
"A94" -> Ecopening("A94", "Dutch", "Dutch, Stonewall with Ba3", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d5 b3 c6 Ba3", "rnbq1rk1/pp2b1pp/2p1pn2/3p1p2/2PP4/BP3NP1/P3PPBP/RN1Q1RK1", "c1a3"),
|
||||
"A95" -> Ecopening("A95", "Dutch", "Dutch, Stonewall", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d5 Nc3 c6", "rnbq1rk1/pp2b1pp/2p1pn2/3p1p2/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "c7c6"),
|
||||
"A96" -> Ecopening("A96", "Dutch", "Dutch, Classical Variation", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d6", "rnbq1rk1/ppp1b1pp/3ppn2/5p2/2PP4/5NP1/PP2PPBP/RNBQ1RK1", "d7d6"),
|
||||
"A97" -> Ecopening("A97", "Dutch", "Dutch, Ilyin-Genevsky", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d6 Nc3 Qe8", "rnb1qrk1/ppp1b1pp/3ppn2/5p2/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "d8e8"),
|
||||
"A98" -> Ecopening("A98", "Dutch", "Dutch, Ilyin-Genevsky Variation with Qc2", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d6 Nc3 Qe8 Qc2", "rnb1qrk1/ppp1b1pp/3ppn2/5p2/2PP4/2N2NP1/PPQ1PPBP/R1B2RK1", "d1c2"),
|
||||
"A99" -> Ecopening("A99", "Dutch", "Dutch, Ilyin-Genevsky Variation with b3", "d4 f5 c4 Nf6 g3 e6 Bg2 Be7 Nf3 O-O O-O d6 Nc3 Qe8 b3", "rnb1qrk1/ppp1b1pp/3ppn2/5p2/2PP4/1PN2NP1/P3PPBP/R1BQ1RK1", "b2b3"),
|
||||
"B00" -> Ecopening("B00", "Uncommon King's Pawn Opening", "Uncommon King's Pawn Opening", "e4", "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR", "e2e4"),
|
||||
"B01" -> Ecopening("B01", "Scandinavian", "Scandinavian", "e4 d5", "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR", "d7d5"),
|
||||
"B02" -> Ecopening("B02", "Alekhine's Defence", "Alekhine's Defence", "e4 Nf6", "rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR", "g8f6"),
|
||||
"B03" -> Ecopening("B03", "Alekhine's Defence", "Alekhine's Defence", "e4 Nf6 e5 Nd5 d4", "rnbqkb1r/pppppppp/8/3nP3/3P4/8/PPP2PPP/RNBQKBNR", "d2d4"),
|
||||
"B04" -> Ecopening("B04", "Alekhine's Defence", "Alekhine's Defence, Modern", "e4 Nf6 e5 Nd5 d4 d6 Nf3", "rnbqkb1r/ppp1pppp/3p4/3nP3/3P4/5N2/PPP2PPP/RNBQKB1R", "g1f3"),
|
||||
"B05" -> Ecopening("B05", "Alekhine's Defence", "Alekhine's Defence, Modern", "e4 Nf6 e5 Nd5 d4 d6 Nf3 Bg4", "rn1qkb1r/ppp1pppp/3p4/3nP3/3P2b1/5N2/PPP2PPP/RNBQKB1R", "c8g4"),
|
||||
"B06" -> Ecopening("B06", "Robatsch", "Robatsch", "e4 g6", "rnbqkbnr/pppppp1p/6p1/8/4P3/8/PPPP1PPP/RNBQKBNR", "g7g6"),
|
||||
"B07" -> Ecopening("B07", "Pirc", "Pirc", "e4 d6 d4 Nf6", "rnbqkb1r/ppp1pppp/3p1n2/8/3PP3/8/PPP2PPP/RNBQKBNR", "g8f6"),
|
||||
"B08" -> Ecopening("B08", "Pirc", "Pirc, Classical", "e4 d6 d4 Nf6 Nc3 g6 Nf3", "rnbqkb1r/ppp1pp1p/3p1np1/8/3PP3/2N2N2/PPP2PPP/R1BQKB1R", "g1f3"),
|
||||
"B09" -> Ecopening("B09", "Pirc", "Pirc, Austrian Attack", "e4 d6 d4 Nf6 Nc3 g6 f4", "rnbqkb1r/ppp1pp1p/3p1np1/8/3PPP2/2N5/PPP3PP/R1BQKBNR", "f2f4"),
|
||||
"B10" -> Ecopening("B10", "Caro-Kann", "Caro-Kann", "e4 c6", "rnbqkbnr/pp1ppppp/2p5/8/4P3/8/PPPP1PPP/RNBQKBNR", "c7c6"),
|
||||
"B11" -> Ecopening("B11", "Caro-Kann", "Caro-Kann, Two Knights, 3...Bg4", "e4 c6 Nc3 d5 Nf3 Bg4", "rn1qkbnr/pp2pppp/2p5/3p4/4P1b1/2N2N2/PPPP1PPP/R1BQKB1R", "c8g4"),
|
||||
"B12" -> Ecopening("B12", "Caro-Kann", "Caro-Kann Defence", "e4 c6 d4", "rnbqkbnr/pp1ppppp/2p5/8/3PP3/8/PPP2PPP/RNBQKBNR", "d2d4"),
|
||||
"B13" -> Ecopening("B13", "Caro-Kann", "Caro-Kann, Exchange", "e4 c6 d4 d5 exd5 cxd5", "rnbqkbnr/pp2pppp/8/3p4/3P4/8/PPP2PPP/RNBQKBNR", "c6d5"),
|
||||
"B14" -> Ecopening("B14", "Caro-Kann", "Caro-Kann, Panov-Botvinnik Attack", "e4 c6 d4 d5 exd5 cxd5 c4 Nf6 Nc3 e6", "rnbqkb1r/pp3ppp/4pn2/3p4/2PP4/2N5/PP3PPP/R1BQKBNR", "e7e6"),
|
||||
"B15" -> Ecopening("B15", "Caro-Kann", "Caro-Kann", "e4 c6 d4 d5 Nc3", "rnbqkbnr/pp2pppp/2p5/3p4/3PP3/2N5/PPP2PPP/R1BQKBNR", "b1c3"),
|
||||
"B16" -> Ecopening("B16", "Caro-Kann", "Caro-Kann, Bronstein-Larsen Variation", "e4 c6 d4 d5 Nc3 dxe4 Nxe4 Nf6 Nxf6+ gxf6", "rnbqkb1r/pp2pp1p/2p2p2/8/3P4/8/PPP2PPP/R1BQKBNR", "g7f6"),
|
||||
"B17" -> Ecopening("B17", "Caro-Kann", "Caro-Kann, Steinitz Variation", "e4 c6 d4 d5 Nc3 dxe4 Nxe4 Nd7", "r1bqkbnr/pp1npppp/2p5/8/3PN3/8/PPP2PPP/R1BQKBNR", "b8d7"),
|
||||
"B18" -> Ecopening("B18", "Caro-Kann", "Caro-Kann, Classical", "e4 c6 d4 d5 Nc3 dxe4 Nxe4 Bf5", "rn1qkbnr/pp2pppp/2p5/5b2/3PN3/8/PPP2PPP/R1BQKBNR", "c8f5"),
|
||||
"B19" -> Ecopening("B19", "Caro-Kann", "Caro-Kann, Classical", "e4 c6 d4 d5 Nc3 dxe4 Nxe4 Bf5 Ng3 Bg6 h4 h6 Nf3 Nd7", "r2qkbnr/pp1nppp1/2p3bp/8/3P3P/5NN1/PPP2PP1/R1BQKB1R", "b8d7"),
|
||||
"B20" -> Ecopening("B20", "Sicilian", "Sicilian", "e4 c5", "rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR", "c7c5"),
|
||||
"B21" -> Ecopening("B21", "Sicilian", "Sicilian, 2.f4 and 2.d4", "e4 c5 f4", "rnbqkbnr/pp1ppppp/8/2p5/4PP2/8/PPPP2PP/RNBQKBNR", "f2f4"),
|
||||
"B22" -> Ecopening("B22", "Sicilian", "Sicilian, Alapin", "e4 c5 c3", "rnbqkbnr/pp1ppppp/8/2p5/4P3/2P5/PP1P1PPP/RNBQKBNR", "c2c3"),
|
||||
"B23" -> Ecopening("B23", "Sicilian", "Sicilian, Closed", "e4 c5 Nc3", "rnbqkbnr/pp1ppppp/8/2p5/4P3/2N5/PPPP1PPP/R1BQKBNR", "b1c3"),
|
||||
"B24" -> Ecopening("B24", "Sicilian", "Sicilian, Closed", "e4 c5 Nc3 Nc6 g3", "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/2N3P1/PPPP1P1P/R1BQKBNR", "g2g3"),
|
||||
"B25" -> Ecopening("B25", "Sicilian", "Sicilian, Closed", "e4 c5 Nc3 Nc6 g3 g6 Bg2 Bg7 d3 d6", "r1bqk1nr/pp2ppbp/2np2p1/2p5/4P3/2NP2P1/PPP2PBP/R1BQK1NR", "d7d6"),
|
||||
"B26" -> Ecopening("B26", "Sicilian", "Sicilian, Closed, 6.Be3", "e4 c5 Nc3 Nc6 g3 g6 Bg2 Bg7 d3 d6 Be3", "r1bqk1nr/pp2ppbp/2np2p1/2p5/4P3/2NPB1P1/PPP2PBP/R2QK1NR", "c1e3"),
|
||||
"B27" -> Ecopening("B27", "Sicilian", "Sicilian", "e4 c5 Nf3", "rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "g1f3"),
|
||||
"B28" -> Ecopening("B28", "Sicilian", "Sicilian, O'Kelly Variation", "e4 c5 Nf3 a6", "rnbqkbnr/1p1ppppp/p7/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "a7a6"),
|
||||
"B29" -> Ecopening("B29", "Sicilian", "Sicilian, Nimzovich-Rubinstein", "e4 c5 Nf3 Nf6", "rnbqkb1r/pp1ppppp/5n2/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "g8f6"),
|
||||
"B30" -> Ecopening("B30", "Sicilian", "Sicilian", "e4 c5 Nf3 Nc6", "r1bqkbnr/pp1ppppp/2n5/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "b8c6"),
|
||||
"B31" -> Ecopening("B31", "Sicilian", "Sicilian, Rossolimo Variation", "e4 c5 Nf3 Nc6 Bb5 g6", "r1bqkbnr/pp1ppp1p/2n3p1/1Bp5/4P3/5N2/PPPP1PPP/RNBQK2R", "g7g6"),
|
||||
"B32" -> Ecopening("B32", "Sicilian", "Sicilian", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 e5", "r1bqkbnr/pp1p1ppp/2n5/4p3/3NP3/8/PPP2PPP/RNBQKB1R", "e7e5"),
|
||||
"B33" -> Ecopening("B33", "Sicilian", "Sicilian", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4", "r1bqkbnr/pp1ppppp/2n5/8/3NP3/8/PPP2PPP/RNBQKB1R", "f3d4"),
|
||||
"B34" -> Ecopening("B34", "Sicilian", "Sicilian, Accelerated Fianchetto", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 Nxc6", "r1bqkbnr/pp1ppp1p/2N3p1/8/4P3/8/PPP2PPP/RNBQKB1R", "d4c6"),
|
||||
"B35" -> Ecopening("B35", "Sicilian", "Sicilian, Accelerated Fianchetto, Modern Variation with Bc4", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 Nc3 Bg7 Be3 Nf6 Bc4", "r1bqk2r/pp1pppbp/2n2np1/8/2BNP3/2N1B3/PPP2PPP/R2QK2R", "f1c4"),
|
||||
"B36" -> Ecopening("B36", "Sicilian", "Sicilian, Accelerated Fianchetto", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 c4", "r1bqkbnr/pp1ppp1p/2n3p1/8/2PNP3/8/PP3PPP/RNBQKB1R", "c2c4"),
|
||||
"B37" -> Ecopening("B37", "Sicilian", "Sicilian, Accelerated Fianchetto", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 c4 Bg7", "r1bqk1nr/pp1pppbp/2n3p1/8/2PNP3/8/PP3PPP/RNBQKB1R", "f8g7"),
|
||||
"B38" -> Ecopening("B38", "Sicilian", "Sicilian, Accelerated Fianchetto, Maroczy Bind, 6.Be3", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 c4 Bg7 Be3", "r1bqk1nr/pp1pppbp/2n3p1/8/2PNP3/4B3/PP3PPP/RN1QKB1R", "c1e3"),
|
||||
"B39" -> Ecopening("B39", "Sicilian", "Sicilian, Accelerated Fianchetto, Breyer Variation", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 g6 c4 Bg7 Be3 Nf6 Nc3 Ng4", "r1bqk2r/pp1pppbp/2n3p1/8/2PNP1n1/2N1B3/PP3PPP/R2QKB1R", "f6g4"),
|
||||
"B40" -> Ecopening("B40", "Sicilian", "Sicilian", "e4 c5 Nf3 e6", "rnbqkbnr/pp1p1ppp/4p3/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "e7e6"),
|
||||
"B41" -> Ecopening("B41", "Sicilian", "Sicilian, Kan", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 a6", "rnbqkbnr/1p1p1ppp/p3p3/8/3NP3/8/PPP2PPP/RNBQKB1R", "a7a6"),
|
||||
"B42" -> Ecopening("B42", "Sicilian", "Sicilian, Kan", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 a6 Bd3", "rnbqkbnr/1p1p1ppp/p3p3/8/3NP3/3B4/PPP2PPP/RNBQK2R", "f1d3"),
|
||||
"B43" -> Ecopening("B43", "Sicilian", "Sicilian, Kan, 5.Nc3", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 a6 Nc3", "rnbqkbnr/1p1p1ppp/p3p3/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "b1c3"),
|
||||
"B44" -> Ecopening("B44", "Sicilian", "Sicilian", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6", "r1bqkbnr/pp1p1ppp/2n1p3/8/3NP3/8/PPP2PPP/RNBQKB1R", "b8c6"),
|
||||
"B45" -> Ecopening("B45", "Sicilian", "Sicilian, Taimanov", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3", "r1bqkbnr/pp1p1ppp/2n1p3/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "b1c3"),
|
||||
"B46" -> Ecopening("B46", "Sicilian", "Sicilian, Taimanov Variation", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3 a6", "r1bqkbnr/1p1p1ppp/p1n1p3/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "a7a6"),
|
||||
"B47" -> Ecopening("B47", "Sicilian", "Sicilian, Taimanov (Bastrikov) Variation", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3 Qc7", "r1b1kbnr/ppqp1ppp/2n1p3/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "d8c7"),
|
||||
"B48" -> Ecopening("B48", "Sicilian", "Sicilian, Taimanov Variation", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3 Qc7 Be3", "r1b1kbnr/ppqp1ppp/2n1p3/8/3NP3/2N1B3/PPP2PPP/R2QKB1R", "c1e3"),
|
||||
"B49" -> Ecopening("B49", "Sicilian", "Sicilian, Taimanov Variation", "e4 c5 Nf3 e6 d4 cxd4 Nxd4 Nc6 Nc3 Qc7 Be3 a6 Be2", "r1b1kbnr/1pqp1ppp/p1n1p3/8/3NP3/2N1B3/PPP1BPPP/R2QK2R", "f1e2"),
|
||||
"B50" -> Ecopening("B50", "Sicilian", "Sicilian", "e4 c5 Nf3 d6", "rnbqkbnr/pp2pppp/3p4/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R", "d7d6"),
|
||||
"B51" -> Ecopening("B51", "Sicilian", "Sicilian, Canal-Sokolsky (Rossolimo) Attack", "e4 c5 Nf3 d6 Bb5+", "rnbqkbnr/pp2pppp/3p4/1Bp5/4P3/5N2/PPPP1PPP/RNBQK2R", "f1b5"),
|
||||
"B52" -> Ecopening("B52", "Sicilian", "Sicilian, Canal-Sokolsky (Rossolimo) Attack", "e4 c5 Nf3 d6 Bb5+ Bd7", "rn1qkbnr/pp1bpppp/3p4/1Bp5/4P3/5N2/PPPP1PPP/RNBQK2R", "c8d7"),
|
||||
"B53" -> Ecopening("B53", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Qxd4", "rnbqkbnr/pp2pppp/3p4/8/3QP3/5N2/PPP2PPP/RNB1KB1R", "d1d4"),
|
||||
"B54" -> Ecopening("B54", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Nxd4", "rnbqkbnr/pp2pppp/3p4/8/3NP3/8/PPP2PPP/RNBQKB1R", "f3d4"),
|
||||
"B55" -> Ecopening("B55", "Sicilian", "Sicilian, Prins Variation, Venice Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 f3 e5 Bb5+", "rnbqkb1r/pp3ppp/3p1n2/1B2p3/3NP3/5P2/PPP3PP/RNBQK2R", "f1b5"),
|
||||
"B56" -> Ecopening("B56", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3", "rnbqkb1r/pp2pppp/3p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "b1c3"),
|
||||
"B57" -> Ecopening("B57", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bc4", "r1bqkb1r/pp2pppp/2np1n2/8/2BNP3/2N5/PPP2PPP/R1BQK2R", "f1c4"),
|
||||
"B58" -> Ecopening("B58", "Sicilian", "Sicilian", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 Nf6 Nc3 d6 Be2", "r1bqkb1r/pp2pppp/2np1n2/8/3NP3/2N5/PPP1BPPP/R1BQK2R", "f1e2"),
|
||||
"B59" -> Ecopening("B59", "Sicilian", "Sicilian, Boleslavsky Variation, 7.Nb3", "e4 c5 Nf3 Nc6 d4 cxd4 Nxd4 Nf6 Nc3 d6 Be2 e5 Nb3", "r1bqkb1r/pp3ppp/2np1n2/4p3/4P3/1NN5/PPP1BPPP/R1BQK2R", "d4b3"),
|
||||
"B60" -> Ecopening("B60", "Sicilian", "Sicilian, Richter-Rauzer", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5", "r1bqkb1r/pp2pppp/2np1n2/6B1/3NP3/2N5/PPP2PPP/R2QKB1R", "c1g5"),
|
||||
"B61" -> Ecopening("B61", "Sicilian", "Sicilian, Richter-Rauzer, Larsen Variation, 7.Qd2", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 Bd7 Qd2", "r2qkb1r/pp1bpppp/2np1n2/6B1/3NP3/2N5/PPPQ1PPP/R3KB1R", "d1d2"),
|
||||
"B62" -> Ecopening("B62", "Sicilian", "Sicilian, Richter-Rauzer", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6", "r1bqkb1r/pp3ppp/2nppn2/6B1/3NP3/2N5/PPP2PPP/R2QKB1R", "e7e6"),
|
||||
"B63" -> Ecopening("B63", "Sicilian", "Sicilian, Richter-Rauzer Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2", "r1bqkb1r/pp3ppp/2nppn2/6B1/3NP3/2N5/PPPQ1PPP/R3KB1R", "d1d2"),
|
||||
"B64" -> Ecopening("B64", "Sicilian", "Sicilian, Richter-Rauzer Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 Be7 O-O-O O-O f4", "r1bq1rk1/pp2bppp/2nppn2/6B1/3NPP2/2N5/PPPQ2PP/2KR1B1R", "f2f4"),
|
||||
"B65" -> Ecopening("B65", "Sicilian", "Sicilian, Richter-Rauzer Attack, 7...Be7 Defence, 9...Nxd4", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 Be7 O-O-O O-O f4 Nxd4 Qxd4", "r1bq1rk1/pp2bppp/3ppn2/6B1/3QPP2/2N5/PPP3PP/2KR1B1R", "d2d4"),
|
||||
"B66" -> Ecopening("B66", "Sicilian", "Sicilian, Richter-Rauzer Attack, 7...a6", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 a6", "r1bqkb1r/1p3ppp/p1nppn2/6B1/3NP3/2N5/PPPQ1PPP/R3KB1R", "a7a6"),
|
||||
"B67" -> Ecopening("B67", "Sicilian", "Sicilian, Richter-Rauzer Attack, 7...a6 Defence, 8...Bd7", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 a6 O-O-O Bd7", "r2qkb1r/1p1b1ppp/p1nppn2/6B1/3NP3/2N5/PPPQ1PPP/2KR1B1R", "c8d7"),
|
||||
"B68" -> Ecopening("B68", "Sicilian", "Sicilian, Richter-Rauzer Attack, 7...a6 Defence, 9...Be7", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 a6 O-O-O Bd7 f4 Be7", "r2qk2r/1p1bbppp/p1nppn2/6B1/3NPP2/2N5/PPPQ2PP/2KR1B1R", "f8e7"),
|
||||
"B69" -> Ecopening("B69", "Sicilian", "Sicilian, Richter-Rauzer Attack, 7...a6 Defence, 11.Bxf6", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 Nc6 Bg5 e6 Qd2 a6 O-O-O Bd7 f4 Be7 Nf3 b5 Bxf6", "r2qk2r/3bbppp/p1nppB2/1p6/4PP2/2N2N2/PPPQ2PP/2KR1B1R", "g5f6"),
|
||||
"B70" -> Ecopening("B70", "Sicilian", "Sicilian, Dragon Variation", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6", "rnbqkb1r/pp2pp1p/3p1np1/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "g7g6"),
|
||||
"B71" -> Ecopening("B71", "Sicilian", "Sicilian, Dragon, Levenfish Variation", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 f4", "rnbqkb1r/pp2pp1p/3p1np1/8/3NPP2/2N5/PPP3PP/R1BQKB1R", "f2f4"),
|
||||
"B72" -> Ecopening("B72", "Sicilian", "Sicilian, Dragon", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3", "rnbqkb1r/pp2pp1p/3p1np1/8/3NP3/2N1B3/PPP2PPP/R2QKB1R", "c1e3"),
|
||||
"B73" -> Ecopening("B73", "Sicilian", "Sicilian, Dragon, Classical", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 Be2 Nc6 O-O", "r1bqk2r/pp2ppbp/2np1np1/8/3NP3/2N1B3/PPP1BPPP/R2Q1RK1", "e1g1"),
|
||||
"B74" -> Ecopening("B74", "Sicilian", "Sicilian, Dragon, Classical", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 Be2 Nc6 O-O O-O Nb3", "r1bq1rk1/pp2ppbp/2np1np1/8/4P3/1NN1B3/PPP1BPPP/R2Q1RK1", "d4b3"),
|
||||
"B75" -> Ecopening("B75", "Sicilian", "Sicilian, Dragon, Yugoslav Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3", "rnbqk2r/pp2ppbp/3p1np1/8/3NP3/2N1BP2/PPP3PP/R2QKB1R", "f2f3"),
|
||||
"B76" -> Ecopening("B76", "Sicilian", "Sicilian, Dragon, Yugoslav Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3 O-O", "rnbq1rk1/pp2ppbp/3p1np1/8/3NP3/2N1BP2/PPP3PP/R2QKB1R", "e8g8"),
|
||||
"B77" -> Ecopening("B77", "Sicilian", "Sicilian, Dragon, Yugoslav Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3 O-O Qd2 Nc6 Bc4", "r1bq1rk1/pp2ppbp/2np1np1/8/2BNP3/2N1BP2/PPPQ2PP/R3K2R", "f1c4"),
|
||||
"B78" -> Ecopening("B78", "Sicilian", "Sicilian, Dragon, Yugoslav Attack, 10.castle long", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3 O-O Qd2 Nc6 Bc4 Bd7 O-O-O", "r2q1rk1/pp1bppbp/2np1np1/8/2BNP3/2N1BP2/PPPQ2PP/2KR3R", "e1c1"),
|
||||
"B79" -> Ecopening("B79", "Sicilian", "Sicilian, Dragon, Yugoslav Attack, 12.h4", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6 Be3 Bg7 f3 O-O Qd2 Nc6 Bc4 Bd7 O-O-O Qa5 Bb3 Rfc8 h4", "r1r3k1/pp1bppbp/2np1np1/q7/3NP2P/1BN1BP2/PPPQ2P1/2KR3R", "h2h4"),
|
||||
"B80" -> Ecopening("B80", "Sicilian", "Sicilian, Scheveningen", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6", "rnbqkb1r/pp3ppp/3ppn2/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "e7e6"),
|
||||
"B81" -> Ecopening("B81", "Sicilian", "Sicilian, Scheveningen, Keres Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 g4", "rnbqkb1r/pp3ppp/3ppn2/8/3NP1P1/2N5/PPP2P1P/R1BQKB1R", "g2g4"),
|
||||
"B82" -> Ecopening("B82", "Sicilian", "Sicilian, Scheveningen", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 f4", "rnbqkb1r/pp3ppp/3ppn2/8/3NPP2/2N5/PPP3PP/R1BQKB1R", "f2f4"),
|
||||
"B83" -> Ecopening("B83", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Be2", "rnbqkb1r/pp3ppp/3ppn2/8/3NP3/2N5/PPP1BPPP/R1BQK2R", "f1e2"),
|
||||
"B84" -> Ecopening("B84", "Sicilian", "Sicilian, Scheveningen", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Be2 a6", "rnbqkb1r/1p3ppp/p2ppn2/8/3NP3/2N5/PPP1BPPP/R1BQK2R", "a7a6"),
|
||||
"B85" -> Ecopening("B85", "Sicilian", "Sicilian, Scheveningen, Classical", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Be2 a6 O-O Qc7 f4 Nc6", "r1b1kb1r/1pq2ppp/p1nppn2/8/3NPP2/2N5/PPP1B1PP/R1BQ1RK1", "b8c6"),
|
||||
"B86" -> Ecopening("B86", "Sicilian", "Sicilian, Fischer-Sozin Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Bc4", "rnbqkb1r/pp3ppp/3ppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R", "f1c4"),
|
||||
"B87" -> Ecopening("B87", "Sicilian", "Sicilian, Fischer-Sozin with ...a6 and ...b5", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Bc4 a6 Bb3 b5", "rnbqkb1r/5ppp/p2ppn2/1p6/3NP3/1BN5/PPP2PPP/R1BQK2R", "b7b5"),
|
||||
"B88" -> Ecopening("B88", "Sicilian", "Sicilian, Fischer-Sozin Attack", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Bc4 Nc6", "r1bqkb1r/pp3ppp/2nppn2/8/2BNP3/2N5/PPP2PPP/R1BQK2R", "b8c6"),
|
||||
"B89" -> Ecopening("B89", "Sicilian", "Sicilian", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 e6 Bc4 Nc6 Be3", "r1bqkb1r/pp3ppp/2nppn2/8/2BNP3/2N1B3/PPP2PPP/R2QK2R", "c1e3"),
|
||||
"B90" -> Ecopening("B90", "Sicilian", "Sicilian, Najdorf", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6", "rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP2PPP/R1BQKB1R", "a7a6"),
|
||||
"B91" -> Ecopening("B91", "Sicilian", "Sicilian, Najdorf, Zagreb (Fianchetto) Variation", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 g3", "rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N3P1/PPP2P1P/R1BQKB1R", "g2g3"),
|
||||
"B92" -> Ecopening("B92", "Sicilian", "Sicilian, Najdorf, Opocensky Variation", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Be2", "rnbqkb1r/1p2pppp/p2p1n2/8/3NP3/2N5/PPP1BPPP/R1BQK2R", "f1e2"),
|
||||
"B93" -> Ecopening("B93", "Sicilian", "Sicilian, Najdorf, 6.f4", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 f4", "rnbqkb1r/1p2pppp/p2p1n2/8/3NPP2/2N5/PPP3PP/R1BQKB1R", "f2f4"),
|
||||
"B94" -> Ecopening("B94", "Sicilian", "Sicilian, Najdorf", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5", "rnbqkb1r/1p2pppp/p2p1n2/6B1/3NP3/2N5/PPP2PPP/R2QKB1R", "c1g5"),
|
||||
"B95" -> Ecopening("B95", "Sicilian", "Sicilian, Najdorf, 6...e6", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6", "rnbqkb1r/1p3ppp/p2ppn2/6B1/3NP3/2N5/PPP2PPP/R2QKB1R", "e7e6"),
|
||||
"B96" -> Ecopening("B96", "Sicilian", "Sicilian, Najdorf", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6 f4", "rnbqkb1r/1p3ppp/p2ppn2/6B1/3NPP2/2N5/PPP3PP/R2QKB1R", "f2f4"),
|
||||
"B97" -> Ecopening("B97", "Sicilian", "Sicilian, Najdorf", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6 f4 Qb6", "rnb1kb1r/1p3ppp/pq1ppn2/6B1/3NPP2/2N5/PPP3PP/R2QKB1R", "d8b6"),
|
||||
"B98" -> Ecopening("B98", "Sicilian", "Sicilian, Najdorf", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6 f4 Be7", "rnbqk2r/1p2bppp/p2ppn2/6B1/3NPP2/2N5/PPP3PP/R2QKB1R", "f8e7"),
|
||||
"B99" -> Ecopening("B99", "Sicilian", "Sicilian, Najdorf, 7...Be7 Main line", "e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 a6 Bg5 e6 f4 Be7 Qf3 Qc7 O-O-O Nbd7", "r1b1k2r/1pqnbppp/p2ppn2/6B1/3NPP2/2N2Q2/PPP3PP/2KR1B1R", "b8d7"),
|
||||
"C00" -> Ecopening("C00", "French", "French Defence", "e4 e6", "rnbqkbnr/pppp1ppp/4p3/8/4P3/8/PPPP1PPP/RNBQKBNR", "e7e6"),
|
||||
"C01" -> Ecopening("C01", "French", "French, Exchange", "e4 e6 d4 d5 exd5 exd5 Nc3 Nf6 Bg5", "rnbqkb1r/ppp2ppp/5n2/3p2B1/3P4/2N5/PPP2PPP/R2QKBNR", "c1g5"),
|
||||
"C02" -> Ecopening("C02", "French", "French, Advance", "e4 e6 d4 d5 e5", "rnbqkbnr/ppp2ppp/4p3/3pP3/3P4/8/PPP2PPP/RNBQKBNR", "e4e5"),
|
||||
"C03" -> Ecopening("C03", "French", "French, Tarrasch", "e4 e6 d4 d5 Nd2", "rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/8/PPPN1PPP/R1BQKBNR", "b1d2"),
|
||||
"C04" -> Ecopening("C04", "French", "French, Tarrasch, Guimard Main line", "e4 e6 d4 d5 Nd2 Nc6 Ngf3 Nf6", "r1bqkb1r/ppp2ppp/2n1pn2/3p4/3PP3/5N2/PPPN1PPP/R1BQKB1R", "g8f6"),
|
||||
"C05" -> Ecopening("C05", "French", "French, Tarrasch", "e4 e6 d4 d5 Nd2 Nf6", "rnbqkb1r/ppp2ppp/4pn2/3p4/3PP3/8/PPPN1PPP/R1BQKBNR", "g8f6"),
|
||||
"C06" -> Ecopening("C06", "French", "French, Tarrasch", "e4 e6 d4 d5 Nd2 Nf6 e5 Nfd7 Bd3 c5 c3 Nc6 Ne2 cxd4 cxd4", "r1bqkb1r/pp1n1ppp/2n1p3/3pP3/3P4/3B4/PP1NNPPP/R1BQK2R", "c3d4"),
|
||||
"C07" -> Ecopening("C07", "French", "French, Tarrasch", "e4 e6 d4 d5 Nd2 c5", "rnbqkbnr/pp3ppp/4p3/2pp4/3PP3/8/PPPN1PPP/R1BQKBNR", "c7c5"),
|
||||
"C08" -> Ecopening("C08", "French", "French, Tarrasch, Open, 4.ed ed", "e4 e6 d4 d5 Nd2 c5 exd5 exd5", "rnbqkbnr/pp3ppp/8/2pp4/3P4/8/PPPN1PPP/R1BQKBNR", "e6d5"),
|
||||
"C09" -> Ecopening("C09", "French", "French, Tarrasch, Open Variation, Main line", "e4 e6 d4 d5 Nd2 c5 exd5 exd5 Ngf3 Nc6", "r1bqkbnr/pp3ppp/2n5/2pp4/3P4/5N2/PPPN1PPP/R1BQKB1R", "b8c6"),
|
||||
"C10" -> Ecopening("C10", "French", "French", "e4 e6 d4 d5 Nc3", "rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/2N5/PPP2PPP/R1BQKBNR", "b1c3"),
|
||||
"C11" -> Ecopening("C11", "French", "French", "e4 e6 d4 d5 Nc3 Nf6", "rnbqkb1r/ppp2ppp/4pn2/3p4/3PP3/2N5/PPP2PPP/R1BQKBNR", "g8f6"),
|
||||
"C12" -> Ecopening("C12", "French", "French, McCutcheon", "e4 e6 d4 d5 Nc3 Nf6 Bg5 Bb4", "rnbqk2r/ppp2ppp/4pn2/3p2B1/1b1PP3/2N5/PPP2PPP/R2QKBNR", "f8b4"),
|
||||
"C13" -> Ecopening("C13", "French", "French", "e4 e6 d4 d5 Nc3 Nf6 Bg5 Be7", "rnbqk2r/ppp1bppp/4pn2/3p2B1/3PP3/2N5/PPP2PPP/R2QKBNR", "f8e7"),
|
||||
"C14" -> Ecopening("C14", "French", "French, Classical", "e4 e6 d4 d5 Nc3 Nf6 Bg5 Be7 e5 Nfd7 Bxe7 Qxe7", "rnb1k2r/pppnqppp/4p3/3pP3/3P4/2N5/PPP2PPP/R2QKBNR", "d8e7"),
|
||||
"C15" -> Ecopening("C15", "French", "French, Winawer", "e4 e6 d4 d5 Nc3 Bb4", "rnbqk1nr/ppp2ppp/4p3/3p4/1b1PP3/2N5/PPP2PPP/R1BQKBNR", "f8b4"),
|
||||
"C16" -> Ecopening("C16", "French", "French, Winawer", "e4 e6 d4 d5 Nc3 Bb4 e5", "rnbqk1nr/ppp2ppp/4p3/3pP3/1b1P4/2N5/PPP2PPP/R1BQKBNR", "e4e5"),
|
||||
"C17" -> Ecopening("C17", "French", "French, Winawer, Advance", "e4 e6 d4 d5 Nc3 Bb4 e5 c5", "rnbqk1nr/pp3ppp/4p3/2ppP3/1b1P4/2N5/PPP2PPP/R1BQKBNR", "c7c5"),
|
||||
"C18" -> Ecopening("C18", "French", "French, Winawer", "e4 e6 d4 d5 Nc3 Bb4 e5 c5 a3 Bxc3+ bxc3", "rnbqk1nr/pp3ppp/4p3/2ppP3/3P4/P1P5/2P2PPP/R1BQKBNR", "b2c3"),
|
||||
"C19" -> Ecopening("C19", "French", "French, Winawer, Advance", "e4 e6 d4 d5 Nc3 Bb4 e5 c5 a3 Bxc3+ bxc3 Ne7", "rnbqk2r/pp2nppp/4p3/2ppP3/3P4/P1P5/2P2PPP/R1BQKBNR", "g8e7"),
|
||||
"C20" -> Ecopening("C20", "King's Pawn Game", "King's Pawn Game", "e4 e5", "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR", "e7e5"),
|
||||
"C21" -> Ecopening("C21", "Center Game", "Center Game", "e4 e5 d4 exd4", "rnbqkbnr/pppp1ppp/8/8/3pP3/8/PPP2PPP/RNBQKBNR", "e5d4"),
|
||||
"C22" -> Ecopening("C22", "Center Game", "Center Game", "e4 e5 d4 exd4 Qxd4 Nc6", "r1bqkbnr/pppp1ppp/2n5/8/3QP3/8/PPP2PPP/RNB1KBNR", "b8c6"),
|
||||
"C23" -> Ecopening("C23", "Bishop's Opening", "Bishop's Opening", "e4 e5 Bc4", "rnbqkbnr/pppp1ppp/8/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR", "f1c4"),
|
||||
"C24" -> Ecopening("C24", "Bishop's Opening", "Bishop's Opening", "e4 e5 Bc4 Nf6", "rnbqkb1r/pppp1ppp/5n2/4p3/2B1P3/8/PPPP1PPP/RNBQK1NR", "g8f6"),
|
||||
"C25" -> Ecopening("C25", "Vienna", "Vienna", "e4 e5 Nc3", "rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR", "b1c3"),
|
||||
"C26" -> Ecopening("C26", "Vienna", "Vienna", "e4 e5 Nc3 Nf6", "rnbqkb1r/pppp1ppp/5n2/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR", "g8f6"),
|
||||
"C27" -> Ecopening("C27", "Vienna Game", "Vienna Game", "e4 e5 Nc3 Nf6 Bc4 Nxe4", "rnbqkb1r/pppp1ppp/8/4p3/2B1n3/2N5/PPPP1PPP/R1BQK1NR", "f6e4"),
|
||||
"C28" -> Ecopening("C28", "Vienna Game", "Vienna Game", "e4 e5 Nc3 Nf6 Bc4 Nc6", "r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/2N5/PPPP1PPP/R1BQK1NR", "b8c6"),
|
||||
"C29" -> Ecopening("C29", "Vienna Gambit", "Vienna Gambit", "e4 e5 Nc3 Nf6 f4", "rnbqkb1r/pppp1ppp/5n2/4p3/4PP2/2N5/PPPP2PP/R1BQKBNR", "f2f4"),
|
||||
"C30" -> Ecopening("C30", "King's Gambit Declined", "King's Gambit Declined", "e4 e5 f4", "rnbqkbnr/pppp1ppp/8/4p3/4PP2/8/PPPP2PP/RNBQKBNR", "f2f4"),
|
||||
"C31" -> Ecopening("C31", "King's Gambit Declined", "King's Gambit Declined, Falkbeer Counter Gambit", "e4 e5 f4 d5", "rnbqkbnr/ppp2ppp/8/3pp3/4PP2/8/PPPP2PP/RNBQKBNR", "d7d5"),
|
||||
"C32" -> Ecopening("C32", "King's Gambit Declined", "King's Gambit Declined, Falkbeer Counter Gambit", "e4 e5 f4 d5 exd5 e4 d3 Nf6", "rnbqkb1r/ppp2ppp/5n2/3P4/4pP2/3P4/PPP3PP/RNBQKBNR", "g8f6"),
|
||||
"C33" -> Ecopening("C33", "King's Gambit Accepted", "King's Gambit Accepted", "e4 e5 f4 exf4", "rnbqkbnr/pppp1ppp/8/8/4Pp2/8/PPPP2PP/RNBQKBNR", "e5f4"),
|
||||
"C34" -> Ecopening("C34", "King's Gambit Accepted", "King's Gambit Accepted", "e4 e5 f4 exf4 Nf3", "rnbqkbnr/pppp1ppp/8/8/4Pp2/5N2/PPPP2PP/RNBQKB1R", "g1f3"),
|
||||
"C35" -> Ecopening("C35", "King's Gambit Accepted", "King's Gambit Accepted, Cunningham", "e4 e5 f4 exf4 Nf3 Be7", "rnbqk1nr/ppppbppp/8/8/4Pp2/5N2/PPPP2PP/RNBQKB1R", "f8e7"),
|
||||
"C36" -> Ecopening("C36", "King's Gambit Accepted", "King's Gambit Accepted, Abbazia Defence", "e4 e5 f4 exf4 Nf3 d5", "rnbqkbnr/ppp2ppp/8/3p4/4Pp2/5N2/PPPP2PP/RNBQKB1R", "d7d5"),
|
||||
"C37" -> Ecopening("C37", "King's Gambit Accepted", "King's Gambit Accepted", "e4 e5 f4 exf4 Nf3 g5 Nc3", "rnbqkbnr/pppp1p1p/8/6p1/4Pp2/2N2N2/PPPP2PP/R1BQKB1R", "b1c3"),
|
||||
"C38" -> Ecopening("C38", "King's Gambit Accepted", "King's Gambit Accepted", "e4 e5 f4 exf4 Nf3 g5 Bc4 Bg7", "rnbqk1nr/pppp1pbp/8/6p1/2B1Pp2/5N2/PPPP2PP/RNBQK2R", "f8g7"),
|
||||
"C39" -> Ecopening("C39", "King's Gambit Accepted", "King's Gambit Accepted", "e4 e5 f4 exf4 Nf3 g5 h4", "rnbqkbnr/pppp1p1p/8/6p1/4Pp1P/5N2/PPPP2P1/RNBQKB1R", "h2h4"),
|
||||
"C40" -> Ecopening("C40", "King's Knight Opening", "King's Knight Opening", "e4 e5 Nf3", "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R", "g1f3"),
|
||||
"C41" -> Ecopening("C41", "Philidor Defence", "Philidor Defence", "e4 e5 Nf3 d6", "rnbqkbnr/ppp2ppp/3p4/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R", "d7d6"),
|
||||
"C42" -> Ecopening("C42", "Petrov Defence", "Petrov Defence", "e4 e5 Nf3 Nf6", "rnbqkb1r/pppp1ppp/5n2/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R", "g8f6"),
|
||||
"C43" -> Ecopening("C43", "Petrov", "Petrov, Modern Attack", "e4 e5 Nf3 Nf6 d4 exd4 e5 Ne4 Qxd4", "rnbqkb1r/pppp1ppp/8/4P3/3Qn3/5N2/PPP2PPP/RNB1KB1R", "d1d4"),
|
||||
"C44" -> Ecopening("C44", "King's Pawn Game", "King's Pawn Game", "e4 e5 Nf3 Nc6", "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R", "b8c6"),
|
||||
"C45" -> Ecopening("C45", "Scotch Game", "Scotch Game", "e4 e5 Nf3 Nc6 d4 exd4 Nxd4", "r1bqkbnr/pppp1ppp/2n5/8/3NP3/8/PPP2PPP/RNBQKB1R", "f3d4"),
|
||||
"C46" -> Ecopening("C46", "Three Knights", "Three Knights", "e4 e5 Nf3 Nc6 Nc3", "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/2N2N2/PPPP1PPP/R1BQKB1R", "b1c3"),
|
||||
"C47" -> Ecopening("C47", "Four Knights", "Four Knights", "e4 e5 Nf3 Nc6 Nc3 Nf6", "r1bqkb1r/pppp1ppp/2n2n2/4p3/4P3/2N2N2/PPPP1PPP/R1BQKB1R", "g8f6"),
|
||||
"C48" -> Ecopening("C48", "Four Knights", "Four Knights", "e4 e5 Nf3 Nc6 Nc3 Nf6 Bb5", "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/2N2N2/PPPP1PPP/R1BQK2R", "f1b5"),
|
||||
"C49" -> Ecopening("C49", "Four Knights", "Four Knights", "e4 e5 Nf3 Nc6 Nc3 Nf6 Bb5 Bb4", "r1bqk2r/pppp1ppp/2n2n2/1B2p3/1b2P3/2N2N2/PPPP1PPP/R1BQK2R", "f8b4"),
|
||||
"C50" -> Ecopening("C50", "Giuoco Piano", "Giuoco Piano", "e4 e5 Nf3 Nc6 Bc4 Bc5", "r1bqk1nr/pppp1ppp/2n5/2b1p3/2B1P3/5N2/PPPP1PPP/RNBQK2R", "f8c5"),
|
||||
"C51" -> Ecopening("C51", "Evans Gambit", "Evans Gambit", "e4 e5 Nf3 Nc6 Bc4 Bc5 b4", "r1bqk1nr/pppp1ppp/2n5/2b1p3/1PB1P3/5N2/P1PP1PPP/RNBQK2R", "b2b4"),
|
||||
"C52" -> Ecopening("C52", "Evans Gambit", "Evans Gambit", "e4 e5 Nf3 Nc6 Bc4 Bc5 b4 Bxb4 c3 Ba5", "r1bqk1nr/pppp1ppp/2n5/b3p3/2B1P3/2P2N2/P2P1PPP/RNBQK2R", "b4a5"),
|
||||
"C53" -> Ecopening("C53", "Giuoco Piano", "Giuoco Piano", "e4 e5 Nf3 Nc6 Bc4 Bc5 c3", "r1bqk1nr/pppp1ppp/2n5/2b1p3/2B1P3/2P2N2/PP1P1PPP/RNBQK2R", "c2c3"),
|
||||
"C54" -> Ecopening("C54", "Giuoco Piano", "Giuoco Piano", "e4 e5 Nf3 Nc6 Bc4 Bc5 c3 Nf6 d4 exd4 cxd4", "r1bqk2r/pppp1ppp/2n2n2/2b5/2BPP3/5N2/PP3PPP/RNBQK2R", "c3d4"),
|
||||
"C55" -> Ecopening("C55", "Two Knights", "Two Knights Defence", "e4 e5 Nf3 Nc6 Bc4 Nf6", "r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R", "g8f6"),
|
||||
"C56" -> Ecopening("C56", "Two Knights", "Two Knights", "e4 e5 Nf3 Nc6 Bc4 Nf6 d4 exd4 O-O Nxe4", "r1bqkb1r/pppp1ppp/2n5/8/2Bpn3/5N2/PPP2PPP/RNBQ1RK1", "f6e4"),
|
||||
"C57" -> Ecopening("C57", "Two Knights", "Two Knights", "e4 e5 Nf3 Nc6 Bc4 Nf6 Ng5", "r1bqkb1r/pppp1ppp/2n2n2/4p1N1/2B1P3/8/PPPP1PPP/RNBQK2R", "f3g5"),
|
||||
"C58" -> Ecopening("C58", "Two Knights", "Two Knights", "e4 e5 Nf3 Nc6 Bc4 Nf6 Ng5 d5 exd5 Na5", "r1bqkb1r/ppp2ppp/5n2/n2Pp1N1/2B5/8/PPPP1PPP/RNBQK2R", "c6a5"),
|
||||
"C59" -> Ecopening("C59", "Two Knights", "Two Knights", "e4 e5 Nf3 Nc6 Bc4 Nf6 Ng5 d5 exd5 Na5 Bb5+ c6 dxc6 bxc6 Be2 h6", "r1bqkb1r/p4pp1/2p2n1p/n3p1N1/8/8/PPPPBPPP/RNBQK2R", "h7h6"),
|
||||
"C60" -> Ecopening("C60", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5", "r1bqkbnr/pppp1ppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R", "f1b5"),
|
||||
"C61" -> Ecopening("C61", "Ruy Lopez", "Ruy Lopez, Bird's Defence", "e4 e5 Nf3 Nc6 Bb5 Nd4", "r1bqkbnr/pppp1ppp/8/1B2p3/3nP3/5N2/PPPP1PPP/RNBQK2R", "c6d4"),
|
||||
"C62" -> Ecopening("C62", "Ruy Lopez", "Ruy Lopez, Old Steinitz Defence", "e4 e5 Nf3 Nc6 Bb5 d6", "r1bqkbnr/ppp2ppp/2np4/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R", "d7d6"),
|
||||
"C63" -> Ecopening("C63", "Ruy Lopez", "Ruy Lopez, Schliemann Defence", "e4 e5 Nf3 Nc6 Bb5 f5", "r1bqkbnr/pppp2pp/2n5/1B2pp2/4P3/5N2/PPPP1PPP/RNBQK2R", "f7f5"),
|
||||
"C64" -> Ecopening("C64", "Ruy Lopez", "Ruy Lopez, Classical", "e4 e5 Nf3 Nc6 Bb5 Bc5", "r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R", "f8c5"),
|
||||
"C65" -> Ecopening("C65", "Ruy Lopez", "Ruy Lopez, Berlin Defence", "e4 e5 Nf3 Nc6 Bb5 Nf6", "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R", "g8f6"),
|
||||
"C66" -> Ecopening("C66", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 Nf6 O-O d6", "r1bqkb1r/ppp2ppp/2np1n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQ1RK1", "d7d6"),
|
||||
"C67" -> Ecopening("C67", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 Nf6 O-O Nxe4", "r1bqkb1r/pppp1ppp/2n5/1B2p3/4n3/5N2/PPPP1PPP/RNBQ1RK1", "f6e4"),
|
||||
"C68" -> Ecopening("C68", "Ruy Lopez", "Ruy Lopez, Exchange", "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6", "r1bqkbnr/1ppp1ppp/p1B5/4p3/4P3/5N2/PPPP1PPP/RNBQK2R", "b5c6"),
|
||||
"C69" -> Ecopening("C69", "Ruy Lopez", "Ruy Lopez, Exchange, Gligoric Variation, 6.d4", "e4 e5 Nf3 Nc6 Bb5 a6 Bxc6 dc O-O f6 d4", "r1bqkbnr/1ppp2pp/p1B2p2/4p3/3PP3/5N2/PPP2PPP/RNBQK2R", "d2d4"),
|
||||
"C70" -> Ecopening("C70", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4", "r1bqkbnr/1ppp1ppp/p1n5/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R", "b5a4"),
|
||||
"C71" -> Ecopening("C71", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6", "r1bqkbnr/1pp2ppp/p1np4/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R", "d7d6"),
|
||||
"C72" -> Ecopening("C72", "Ruy Lopez", "Ruy Lopez, Modern Steinitz Defence, 5.O-O", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 O-O", "r1bqkbnr/1pp2ppp/p1np4/4p3/B3P3/5N2/PPPP1PPP/RNBQ1RK1", "e1g1"),
|
||||
"C73" -> Ecopening("C73", "Ruy Lopez", "Ruy Lopez, Modern Steinitz Defence", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 Bxc6+ bxc6 d4", "r1bqkbnr/2p2ppp/p1pp4/4p3/3PP3/5N2/PPP2PPP/RNBQK2R", "d2d4"),
|
||||
"C74" -> Ecopening("C74", "Ruy Lopez", "Ruy Lopez, Modern Steinitz Defence", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 c3", "r1bqkbnr/1pp2ppp/p1np4/4p3/B3P3/2P2N2/PP1P1PPP/RNBQK2R", "c2c3"),
|
||||
"C75" -> Ecopening("C75", "Ruy Lopez", "Ruy Lopez, Modern Steinitz Defence", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 c3 Bd7", "r2qkbnr/1ppb1ppp/p1np4/4p3/B3P3/2P2N2/PP1P1PPP/RNBQK2R", "c8d7"),
|
||||
"C76" -> Ecopening("C76", "Ruy Lopez", "Ruy Lopez, Modern Steinitz Defence, Fianchetto Variation", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 d6 c3 Bd7 d4 g6", "r2qkbnr/1ppb1p1p/p1np2p1/4p3/B2PP3/2P2N2/PP3PPP/RNBQK2R", "g7g6"),
|
||||
"C77" -> Ecopening("C77", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6", "r1bqkb1r/1ppp1ppp/p1n2n2/4p3/B3P3/5N2/PPPP1PPP/RNBQK2R", "g8f6"),
|
||||
"C78" -> Ecopening("C78", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O", "r1bqkb1r/1ppp1ppp/p1n2n2/4p3/B3P3/5N2/PPPP1PPP/RNBQ1RK1", "e1g1"),
|
||||
"C79" -> Ecopening("C79", "Ruy Lopez", "Ruy Lopez, Steinitz Defence Deferred", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O d6", "r1bqkb1r/1pp2ppp/p1np1n2/4p3/B3P3/5N2/PPPP1PPP/RNBQ1RK1", "d7d6"),
|
||||
"C80" -> Ecopening("C80", "Ruy Lopez", "Ruy Lopez, Open", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Nxe4", "r1bqkb1r/1ppp1ppp/p1n5/4p3/B3n3/5N2/PPPP1PPP/RNBQ1RK1", "f6e4"),
|
||||
"C81" -> Ecopening("C81", "Ruy Lopez", "Ruy Lopez, Open, Howell Attack", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Nxe4 d4 b5 Bb3 d5 dxe5 Be6", "r2qkb1r/2p2ppp/p1n1b3/1p1pP3/4n3/1B3N2/PPP2PPP/RNBQ1RK1", "c8e6"),
|
||||
"C82" -> Ecopening("C82", "Ruy Lopez", "Ruy Lopez, Open", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Nxe4 d4 b5 Bb3 d5 dxe5 Be6 c3", "r2qkb1r/2p2ppp/p1n1b3/1p1pP3/4n3/1BP2N2/PP3PPP/RNBQ1RK1", "c2c3"),
|
||||
"C83" -> Ecopening("C83", "Ruy Lopez", "Ruy Lopez, Open", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Nxe4 d4 b5 Bb3 d5 dxe5 Be6", "r2qkb1r/2p2ppp/p1n1b3/1p1pP3/4n3/1B3N2/PPP2PPP/RNBQ1RK1", "c8e6"),
|
||||
"C84" -> Ecopening("C84", "Ruy Lopez", "Ruy Lopez, Closed", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7", "r1bqk2r/1pppbppp/p1n2n2/4p3/B3P3/5N2/PPPP1PPP/RNBQ1RK1", "f8e7"),
|
||||
"C85" -> Ecopening("C85", "Ruy Lopez", "Ruy Lopez, Exchange Variation Doubly Deferred (DERLD)", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Bxc6 dxc6", "r1bqk2r/1pp1bppp/p1p2n2/4p3/4P3/5N2/PPPP1PPP/RNBQ1RK1", "d7c6"),
|
||||
"C86" -> Ecopening("C86", "Ruy Lopez", "Ruy Lopez, Worrall Attack", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Qe2", "r1bqk2r/1pppbppp/p1n2n2/4p3/B3P3/5N2/PPPPQPPP/RNB2RK1", "d1e2"),
|
||||
"C87" -> Ecopening("C87", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 d6", "r1bqk2r/1pp1bppp/p1np1n2/4p3/B3P3/5N2/PPPP1PPP/RNBQR1K1", "d7d6"),
|
||||
"C88" -> Ecopening("C88", "Ruy Lopez", "Ruy Lopez", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3", "r1bqk2r/2ppbppp/p1n2n2/1p2p3/4P3/1B3N2/PPPP1PPP/RNBQR1K1", "a4b3"),
|
||||
"C89" -> Ecopening("C89", "Ruy Lopez", "Ruy Lopez, Marshall", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d5", "r1bq1rk1/2p1bppp/p1n2n2/1p1pp3/4P3/1BP2N2/PP1P1PPP/RNBQR1K1", "d7d5"),
|
||||
"C90" -> Ecopening("C90", "Ruy Lopez", "Ruy Lopez, Closed", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6", "r1bq1rk1/2p1bppp/p1np1n2/1p2p3/4P3/1BP2N2/PP1P1PPP/RNBQR1K1", "d7d6"),
|
||||
"C91" -> Ecopening("C91", "Ruy Lopez", "Ruy Lopez, Closed", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 d4", "r1bq1rk1/2p1bppp/p1np1n2/1p2p3/3PP3/1BP2N2/PP3PPP/RNBQR1K1", "d2d4"),
|
||||
"C92" -> Ecopening("C92", "Ruy Lopez", "Ruy Lopez, Closed", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3", "r1bq1rk1/2p1bppp/p1np1n2/1p2p3/4P3/1BP2N1P/PP1P1PP1/RNBQR1K1", "h2h3"),
|
||||
"C93" -> Ecopening("C93", "Ruy Lopez", "Ruy Lopez, Closed, Smyslov Defence", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 h6", "r1bq1rk1/2p1bpp1/p1np1n1p/1p2p3/4P3/1BP2N1P/PP1P1PP1/RNBQR1K1", "h7h6"),
|
||||
"C94" -> Ecopening("C94", "Ruy Lopez", "Ruy Lopez, Closed, Breyer Defence", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Nb8", "rnbq1rk1/2p1bppp/p2p1n2/1p2p3/4P3/1BP2N1P/PP1P1PP1/RNBQR1K1", "c6b8"),
|
||||
"C95" -> Ecopening("C95", "Ruy Lopez", "Ruy Lopez, Closed, Breyer", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Nb8 d4", "rnbq1rk1/2p1bppp/p2p1n2/1p2p3/3PP3/1BP2N1P/PP3PP1/RNBQR1K1", "d2d4"),
|
||||
"C96" -> Ecopening("C96", "Ruy Lopez", "Ruy Lopez, Closed", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Na5 Bc2", "r1bq1rk1/2p1bppp/p2p1n2/np2p3/4P3/2P2N1P/PPBP1PP1/RNBQR1K1", "b3c2"),
|
||||
"C97" -> Ecopening("C97", "Ruy Lopez", "Ruy Lopez, Closed, Chigorin", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Na5 Bc2 c5 d4 Qc7", "r1b2rk1/2q1bppp/p2p1n2/npp1p3/3PP3/2P2N1P/PPB2PP1/RNBQR1K1", "d8c7"),
|
||||
"C98" -> Ecopening("C98", "Ruy Lopez", "Ruy Lopez, Closed, Chigorin", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Na5 Bc2 c5 d4 Qc7 Nbd2 Nc6", "r1b2rk1/2q1bppp/p1np1n2/1pp1p3/3PP3/2P2N1P/PPBN1PP1/R1BQR1K1", "a5c6"),
|
||||
"C99" -> Ecopening("C99", "Ruy Lopez", "Ruy Lopez, Closed, Chigorin, 12...cd", "e4 e5 Nf3 Nc6 Bb5 a6 Ba4 Nf6 O-O Be7 Re1 b5 Bb3 O-O c3 d6 h3 Na5 Bc2 c5 d4 Qc7 Nbd2 cxd4 cxd4", "r1b2rk1/2q1bppp/p2p1n2/np2p3/3PP3/5N1P/PPBN1PP1/R1BQR1K1", "c3d4"),
|
||||
"D00" -> Ecopening("D00", "Queen's Pawn Game", "Queen's Pawn Game", "d4 d5", "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR", "d7d5"),
|
||||
"D01" -> Ecopening("D01", "Richter-Veresov Attack", "Richter-Veresov Attack", "d4 d5 Nc3 Nf6 Bg5", "rnbqkb1r/ppp1pppp/5n2/3p2B1/3P4/2N5/PPP1PPPP/R2QKBNR", "c1g5"),
|
||||
"D02" -> Ecopening("D02", "Queen's Pawn Game", "Queen's Pawn Game", "d4 d5 Nf3", "rnbqkbnr/ppp1pppp/8/3p4/3P4/5N2/PPP1PPPP/RNBQKB1R", "g1f3"),
|
||||
"D03" -> Ecopening("D03", "Torre Attack", "Torre Attack (Tartakower Variation)", "d4 d5 Nf3 Nf6 Bg5", "rnbqkb1r/ppp1pppp/5n2/3p2B1/3P4/5N2/PPP1PPPP/RN1QKB1R", "c1g5"),
|
||||
"D04" -> Ecopening("D04", "Queen's Pawn Game", "Queen's Pawn Game", "d4 d5 Nf3 Nf6 e3", "rnbqkb1r/ppp1pppp/5n2/3p4/3P4/4PN2/PPP2PPP/RNBQKB1R", "e2e3"),
|
||||
"D05" -> Ecopening("D05", "Queen's Pawn Game", "Queen's Pawn Game", "d4 d5 Nf3 Nf6 e3 e6", "rnbqkb1r/ppp2ppp/4pn2/3p4/3P4/4PN2/PPP2PPP/RNBQKB1R", "e7e6"),
|
||||
"D06" -> Ecopening("D06", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4", "rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR", "c2c4"),
|
||||
"D07" -> Ecopening("D07", "Queen's Gambit Declined", "Queen's Gambit Declined, Chigorin Defence", "d4 d5 c4 Nc6", "r1bqkbnr/ppp1pppp/2n5/3p4/2PP4/8/PP2PPPP/RNBQKBNR", "b8c6"),
|
||||
"D08" -> Ecopening("D08", "Queen's Gambit Declined", "Queen's Gambit Declined, Albin Counter Gambit", "d4 d5 c4 e5", "rnbqkbnr/ppp2ppp/8/3pp3/2PP4/8/PP2PPPP/RNBQKBNR", "e7e5"),
|
||||
"D09" -> Ecopening("D09", "Queen's Gambit Declined", "Queen's Gambit Declined, Albin Counter Gambit, 5.g3", "d4 d5 c4 e5 dxe5 d4 Nf3 Nc6 g3", "r1bqkbnr/ppp2ppp/2n5/4P3/2Pp4/5NP1/PP2PP1P/RNBQKB1R", "g2g3"),
|
||||
"D10" -> Ecopening("D10", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6", "rnbqkbnr/pp2pppp/2p5/3p4/2PP4/8/PP2PPPP/RNBQKBNR", "c7c6"),
|
||||
"D11" -> Ecopening("D11", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6 Nf3", "rnbqkbnr/pp2pppp/2p5/3p4/2PP4/5N2/PP2PPPP/RNBQKB1R", "g1f3"),
|
||||
"D12" -> Ecopening("D12", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6 Nf3 Nf6 e3 Bf5", "rn1qkb1r/pp2pppp/2p2n2/3p1b2/2PP4/4PN2/PP3PPP/RNBQKB1R", "c8f5"),
|
||||
"D13" -> Ecopening("D13", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav, Exchange Variation", "d4 d5 c4 c6 Nf3 Nf6 cxd5 cxd5", "rnbqkb1r/pp2pppp/5n2/3p4/3P4/5N2/PP2PPPP/RNBQKB1R", "c6d5"),
|
||||
"D14" -> Ecopening("D14", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav, Exchange Variation", "d4 d5 c4 c6 Nf3 Nf6 cxd5 cxd5 Nc3 Nc6 Bf4 Bf5", "r2qkb1r/pp2pppp/2n2n2/3p1b2/3P1B2/2N2N2/PP2PPPP/R2QKB1R", "c8f5"),
|
||||
"D15" -> Ecopening("D15", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6 Nf3 Nf6 Nc3", "rnbqkb1r/pp2pppp/2p2n2/3p4/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "b1c3"),
|
||||
"D16" -> Ecopening("D16", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6 Nf3 Nf6 Nc3 dxc4 a4", "rnbqkb1r/pp2pppp/2p2n2/8/P1pP4/2N2N2/1P2PPPP/R1BQKB1R", "a2a4"),
|
||||
"D17" -> Ecopening("D17", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav", "d4 d5 c4 c6 Nf3 Nf6 Nc3 dxc4 a4 Bf5", "rn1qkb1r/pp2pppp/2p2n2/5b2/P1pP4/2N2N2/1P2PPPP/R1BQKB1R", "c8f5"),
|
||||
"D18" -> Ecopening("D18", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav, Dutch", "d4 d5 c4 c6 Nf3 Nf6 Nc3 dxc4 a4 Bf5 e3", "rn1qkb1r/pp2pppp/2p2n2/5b2/P1pP4/2N1PN2/1P3PPP/R1BQKB1R", "e2e3"),
|
||||
"D19" -> Ecopening("D19", "Queen's Gambit Declined Slav", "Queen's Gambit Declined Slav, Dutch", "d4 d5 c4 c6 Nf3 Nf6 Nc3 dxc4 a4 Bf5 e3 e6 Bxc4 Bb4 O-O O-O Qe2", "rn1q1rk1/pp3ppp/2p1pn2/5b2/PbBP4/2N1PN2/1P2QPPP/R1B2RK1", "d1e2"),
|
||||
"D20" -> Ecopening("D20", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4", "rnbqkbnr/ppp1pppp/8/8/2pP4/8/PP2PPPP/RNBQKBNR", "d5c4"),
|
||||
"D21" -> Ecopening("D21", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3", "rnbqkbnr/ppp1pppp/8/8/2pP4/5N2/PP2PPPP/RNBQKB1R", "g1f3"),
|
||||
"D22" -> Ecopening("D22", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3 a6 e3 Bg4 Bxc4 e6 d5", "rn1qkbnr/1pp2ppp/p3p3/3P4/2B3b1/4PN2/PP3PPP/RNBQK2R", "d4d5"),
|
||||
"D23" -> Ecopening("D23", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3 Nf6", "rnbqkb1r/ppp1pppp/5n2/8/2pP4/5N2/PP2PPPP/RNBQKB1R", "g8f6"),
|
||||
"D24" -> Ecopening("D24", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3 Nf6 Nc3", "rnbqkb1r/ppp1pppp/5n2/8/2pP4/2N2N2/PP2PPPP/R1BQKB1R", "b1c3"),
|
||||
"D25" -> Ecopening("D25", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3 Nf6 e3", "rnbqkb1r/ppp1pppp/5n2/8/2pP4/4PN2/PP3PPP/RNBQKB1R", "e2e3"),
|
||||
"D26" -> Ecopening("D26", "Queen's Gambit Accepted", "Queen's Gambit Accepted", "d4 d5 c4 dxc4 Nf3 Nf6 e3 e6", "rnbqkb1r/ppp2ppp/4pn2/8/2pP4/4PN2/PP3PPP/RNBQKB1R", "e7e6"),
|
||||
"D27" -> Ecopening("D27", "Queen's Gambit Accepted", "Queen's Gambit Accepted, Classical", "d4 d5 c4 dxc4 Nf3 Nf6 e3 e6 Bxc4 c5 O-O a6", "rnbqkb1r/1p3ppp/p3pn2/2p5/2BP4/4PN2/PP3PPP/RNBQ1RK1", "a7a6"),
|
||||
"D28" -> Ecopening("D28", "Queen's Gambit Accepted", "Queen's Gambit Accepted, Classical", "d4 d5 c4 dxc4 Nf3 Nf6 e3 e6 Bxc4 c5 O-O a6 Qe2", "rnbqkb1r/1p3ppp/p3pn2/2p5/2BP4/4PN2/PP2QPPP/RNB2RK1", "d1e2"),
|
||||
"D29" -> Ecopening("D29", "Queen's Gambit Accepted", "Queen's Gambit Accepted, Classical", "d4 d5 c4 dxc4 Nf3 Nf6 e3 e6 Bxc4 c5 O-O a6 Qe2 b5 Bb3 Bb7", "rn1qkb1r/1b3ppp/p3pn2/1pp5/3P4/1B2PN2/PP2QPPP/RNB2RK1", "c8b7"),
|
||||
"D30" -> Ecopening("D30", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6", "rnbqkbnr/ppp2ppp/4p3/3p4/2PP4/8/PP2PPPP/RNBQKBNR", "e7e6"),
|
||||
"D31" -> Ecopening("D31", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3", "rnbqkbnr/ppp2ppp/4p3/3p4/2PP4/2N5/PP2PPPP/R1BQKBNR", "b1c3"),
|
||||
"D32" -> Ecopening("D32", "Queen's Gambit Declined", "Queen's Gambit Declined, Tarrasch", "d4 d5 c4 e6 Nc3 c5", "rnbqkbnr/pp3ppp/4p3/2pp4/2PP4/2N5/PP2PPPP/R1BQKBNR", "c7c5"),
|
||||
"D33" -> Ecopening("D33", "Queen's Gambit Declined", "Queen's Gambit Declined, Tarrasch", "d4 d5 c4 e6 Nc3 c5 cxd5 exd5 Nf3 Nc6 g3", "r1bqkbnr/pp3ppp/2n5/2pp4/3P4/2N2NP1/PP2PP1P/R1BQKB1R", "g2g3"),
|
||||
"D34" -> Ecopening("D34", "Queen's Gambit Declined", "Queen's Gambit Declined, Tarrasch", "d4 d5 c4 e6 Nc3 c5 cxd5 exd5 Nf3 Nc6 g3 Nf6 Bg2 Be7", "r1bqk2r/pp2bppp/2n2n2/2pp4/3P4/2N2NP1/PP2PPBP/R1BQK2R", "f8e7"),
|
||||
"D35" -> Ecopening("D35", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6", "rnbqkb1r/ppp2ppp/4pn2/3p4/2PP4/2N5/PP2PPPP/R1BQKBNR", "g8f6"),
|
||||
"D36" -> Ecopening("D36", "Queen's Gambit Declined", "Queen's Gambit Declined, Exchange, Positional line, 6.Qc2", "d4 d5 c4 e6 Nc3 Nf6 cxd5 exd5 Bg5 c6 Qc2", "rnbqkb1r/pp3ppp/2p2n2/3p2B1/3P4/2N5/PPQ1PPPP/R3KBNR", "d1c2"),
|
||||
"D37" -> Ecopening("D37", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Nf3", "rnbqkb1r/ppp2ppp/4pn2/3p4/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "g1f3"),
|
||||
"D38" -> Ecopening("D38", "Queen's Gambit Declined", "Queen's Gambit Declined, Ragozin Variation", "d4 d5 c4 e6 Nc3 Nf6 Nf3 Bb4", "rnbqk2r/ppp2ppp/4pn2/3p4/1bPP4/2N2N2/PP2PPPP/R1BQKB1R", "f8b4"),
|
||||
"D39" -> Ecopening("D39", "Queen's Gambit Declined", "Queen's Gambit Declined, Ragozin, Vienna Variation", "d4 d5 c4 e6 Nc3 Nf6 Nf3 Bb4 Bg5 dxc4", "rnbqk2r/ppp2ppp/4pn2/6B1/1bpP4/2N2N2/PP2PPPP/R2QKB1R", "d5c4"),
|
||||
"D40" -> Ecopening("D40", "Queen's Gambit Declined", "Queen's Gambit Declined, Semi-Tarrasch", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c5", "rnbqkb1r/pp3ppp/4pn2/2pp4/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "c7c5"),
|
||||
"D41" -> Ecopening("D41", "Queen's Gambit Declined", "Queen's Gambit Declined, Semi-Tarrasch", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c5 cxd5", "rnbqkb1r/pp3ppp/4pn2/2pP4/3P4/2N2N2/PP2PPPP/R1BQKB1R", "c4d5"),
|
||||
"D42" -> Ecopening("D42", "Queen's Gambit Declined", "Queen's Gambit Declined, Semi-Tarrasch, 7.Bd3", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c5 cxd5 Nxd5 e3 Nc6 Bd3", "r1bqkb1r/pp3ppp/2n1p3/2pn4/3P4/2NBPN2/PP3PPP/R1BQK2R", "f1d3"),
|
||||
"D43" -> Ecopening("D43", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6", "rnbqkb1r/pp3ppp/2p1pn2/3p4/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "c7c6"),
|
||||
"D44" -> Ecopening("D44", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 Bg5 dxc4", "rnbqkb1r/pp3ppp/2p1pn2/6B1/2pP4/2N2N2/PP2PPPP/R2QKB1R", "d5c4"),
|
||||
"D45" -> Ecopening("D45", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 e3", "rnbqkb1r/pp3ppp/2p1pn2/3p4/2PP4/2N1PN2/PP3PPP/R1BQKB1R", "e2e3"),
|
||||
"D46" -> Ecopening("D46", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 e3 Nbd7 Bd3", "r1bqkb1r/pp1n1ppp/2p1pn2/3p4/2PP4/2NBPN2/PP3PPP/R1BQK2R", "f1d3"),
|
||||
"D47" -> Ecopening("D47", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 e3 Nbd7 Bd3 dxc4 Bxc4", "r1bqkb1r/pp1n1ppp/2p1pn2/8/2BP4/2N1PN2/PP3PPP/R1BQK2R", "d3c4"),
|
||||
"D48" -> Ecopening("D48", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav, Meran", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 e3 Nbd7 Bd3 dxc4 Bxc4 b5 Bd3 a6", "r1bqkb1r/3n1ppp/p1p1pn2/1p6/3P4/2NBPN2/PP3PPP/R1BQK2R", "a7a6"),
|
||||
"D49" -> Ecopening("D49", "Queen's Gambit Declined Semi-Slav", "Queen's Gambit Declined Semi-Slav, Meran", "d4 d5 c4 e6 Nc3 Nf6 Nf3 c6 e3 Nbd7 Bd3 dxc4 Bxc4 b5 Bd3 a6 e4 c5 e5 cxd4 Nxb5", "r1bqkb1r/3n1ppp/p3pn2/1N2P3/3p4/3B1N2/PP3PPP/R1BQK2R", "c3b5"),
|
||||
"D50" -> Ecopening("D50", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5", "rnbqkb1r/ppp2ppp/4pn2/3p2B1/2PP4/2N5/PP2PPPP/R2QKBNR", "c1g5"),
|
||||
"D51" -> Ecopening("D51", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Nbd7", "r1bqkb1r/pppn1ppp/4pn2/3p2B1/2PP4/2N5/PP2PPPP/R2QKBNR", "b8d7"),
|
||||
"D52" -> Ecopening("D52", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Nbd7 e3 c6 Nf3", "r1bqkb1r/pp1n1ppp/2p1pn2/3p2B1/2PP4/2N1PN2/PP3PPP/R2QKB1R", "g1f3"),
|
||||
"D53" -> Ecopening("D53", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7", "rnbqk2r/ppp1bppp/4pn2/3p2B1/2PP4/2N5/PP2PPPP/R2QKBNR", "f8e7"),
|
||||
"D54" -> Ecopening("D54", "Queen's Gambit Declined", "Queen's Gambit Declined, Anti-Neo-Orthodox Variation", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Rc1", "rnbq1rk1/ppp1bppp/4pn2/3p2B1/2PP4/2N1P3/PP3PPP/2RQKBNR", "a1c1"),
|
||||
"D55" -> Ecopening("D55", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3", "rnbq1rk1/ppp1bppp/4pn2/3p2B1/2PP4/2N1PN2/PP3PPP/R2QKB1R", "g1f3"),
|
||||
"D56" -> Ecopening("D56", "Queen's Gambit Declined", "Queen's Gambit Declined", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 h6 Bh4", "rnbq1rk1/ppp1bpp1/4pn1p/3p4/2PP3B/2N1PN2/PP3PPP/R2QKB1R", "g5h4"),
|
||||
"D57" -> Ecopening("D57", "Queen's Gambit Declined", "Queen's Gambit Declined, Lasker Defence", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 h6 Bh4 Ne4 Bxe7 Qxe7", "rnb2rk1/ppp1qpp1/4p2p/3p4/2PPn3/2N1PN2/PP3PPP/R2QKB1R", "d8e7"),
|
||||
"D58" -> Ecopening("D58", "Queen's Gambit Declined", "Queen's Gambit Declined, Tartakower (Makagonov-Bondarevsky) Syst", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 h6 Bh4 b6", "rnbq1rk1/p1p1bpp1/1p2pn1p/3p4/2PP3B/2N1PN2/PP3PPP/R2QKB1R", "b7b6"),
|
||||
"D59" -> Ecopening("D59", "Queen's Gambit Declined", "Queen's Gambit Declined, Tartakower", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 h6 Bh4 b6 cxd5 Nxd5", "rnbq1rk1/p1p1bpp1/1p2p2p/3n4/3P3B/2N1PN2/PP3PPP/R2QKB1R", "f6d5"),
|
||||
"D60" -> Ecopening("D60", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7", "r1bq1rk1/pppnbppp/4pn2/3p2B1/2PP4/2N1PN2/PP3PPP/R2QKB1R", "b8d7"),
|
||||
"D61" -> Ecopening("D61", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox, Rubinstein Attack", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Qc2", "r1bq1rk1/pppnbppp/4pn2/3p2B1/2PP4/2N1PN2/PPQ2PPP/R3KB1R", "d1c2"),
|
||||
"D62" -> Ecopening("D62", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox, Rubinstein Attack", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Qc2 c5 cxd5", "r1bq1rk1/pp1nbppp/4pn2/2pP2B1/3P4/2N1PN2/PPQ2PPP/R3KB1R", "c4d5"),
|
||||
"D63" -> Ecopening("D63", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1", "r1bq1rk1/pppnbppp/4pn2/3p2B1/2PP4/2N1PN2/PP3PPP/2RQKB1R", "a1c1"),
|
||||
"D64" -> Ecopening("D64", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox, Rubinstein Attack", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Qc2", "r1bq1rk1/pp1nbppp/2p1pn2/3p2B1/2PP4/2N1PN2/PPQ2PPP/2R1KB1R", "d1c2"),
|
||||
"D65" -> Ecopening("D65", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox, Rubinstein Attack, Main line", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Qc2 a6 cxd5", "r1bq1rk1/1p1nbppp/p1p1pn2/3P2B1/3P4/2N1PN2/PPQ2PPP/2R1KB1R", "c4d5"),
|
||||
"D66" -> Ecopening("D66", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence, Bd3 line", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Bd3", "r1bq1rk1/pp1nbppp/2p1pn2/3p2B1/2PP4/2NBPN2/PP3PPP/2RQK2R", "f1d3"),
|
||||
"D67" -> Ecopening("D67", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence, Bd3 line", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Bd3 dxc4 Bxc4 Nd5", "r1bq1rk1/pp1nbppp/2p1p3/3n2B1/2BP4/2N1PN2/PP3PPP/2RQK2R", "f6d5"),
|
||||
"D68" -> Ecopening("D68", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence, Classical", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Bd3 dxc4", "r1bq1rk1/pp1nbppp/2p1pn2/6B1/2pP4/2NBPN2/PP3PPP/2RQK2R", "d5c4"),
|
||||
"D69" -> Ecopening("D69", "Queen's Gambit Declined", "Queen's Gambit Declined, Orthodox Defence, Classical, 13.de", "d4 d5 c4 e6 Nc3 Nf6 Bg5 Be7 e3 O-O Nf3 Nbd7 Rc1 c6 Bd3 dxc4", "r1bq1rk1/pp1nbppp/2p1pn2/6B1/2pP4/2NBPN2/PP3PPP/2RQK2R", "d5c4"),
|
||||
"D70" -> Ecopening("D70", "Neo-Grunfeld Defence", "Neo-Grunfeld Defence", "d4 Nf6 c4 g6 f3 d5", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/5P2/PP2P1PP/RNBQKBNR", "d7d5"),
|
||||
"D71" -> Ecopening("D71", "Neo-Grunfeld", "Neo-Grunfeld", "d4 Nf6 c4 g6 g3 d5", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/6P1/PP2PP1P/RNBQKBNR", "d7d5"),
|
||||
"D72" -> Ecopening("D72", "Neo-Grunfeld", "Neo-Grunfeld, 5.cd, Main line", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 cxd5 Nxd5 e4 Nb6 Ne2", "rnbqk2r/ppp1ppbp/1n4p1/8/3PP3/6P1/PP2NPBP/RNBQK2R", "g1e2"),
|
||||
"D73" -> Ecopening("D73", "Neo-Grunfeld", "Neo-Grunfeld, 5.Nf3", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3", "rnbqk2r/ppp1ppbp/5np1/3p4/2PP4/5NP1/PP2PPBP/RNBQK2R", "g1f3"),
|
||||
"D74" -> Ecopening("D74", "Neo-Grunfeld", "Neo-Grunfeld, 6.cd Nxd5, 7.O-O", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O cxd5 Nxd5 O-O", "rnbq1rk1/ppp1ppbp/6p1/3n4/3P4/5NP1/PP2PPBP/RNBQ1RK1", "e1g1"),
|
||||
"D75" -> Ecopening("D75", "Neo-Grunfeld", "Neo-Grunfeld, 6.cd Nxd5, 7.O-O c5, 8.dxc5", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O cxd5 Nxd5 O-O c5 dxc5", "rnbq1rk1/pp2ppbp/6p1/2Pn4/8/5NP1/PP2PPBP/RNBQ1RK1", "d4c5"),
|
||||
"D76" -> Ecopening("D76", "Neo-Grunfeld", "Neo-Grunfeld, 6.cd Nxd5, 7.O-O Nb6", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O cxd5 Nxd5 O-O Nb6", "rnbq1rk1/ppp1ppbp/1n4p1/8/3P4/5NP1/PP2PPBP/RNBQ1RK1", "d5b6"),
|
||||
"D77" -> Ecopening("D77", "Neo-Grunfeld", "Neo-Grunfeld, 6.O-O", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O O-O", "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP4/5NP1/PP2PPBP/RNBQ1RK1", "e1g1"),
|
||||
"D78" -> Ecopening("D78", "Neo-Grunfeld", "Neo-Grunfeld, 6.O-O c6", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O O-O c6", "rnbq1rk1/pp2ppbp/2p2np1/3p4/2PP4/5NP1/PP2PPBP/RNBQ1RK1", "c7c6"),
|
||||
"D79" -> Ecopening("D79", "Neo-Grunfeld", "Neo-Grunfeld, 6.O-O, Main line", "d4 Nf6 c4 g6 g3 d5 Bg2 Bg7 Nf3 O-O O-O c6 cxd5 cxd5", "rnbq1rk1/pp2ppbp/5np1/3p4/3P4/5NP1/PP2PPBP/RNBQ1RK1", "c6d5"),
|
||||
"D80" -> Ecopening("D80", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/2N5/PP2PPPP/R1BQKBNR", "d7d5"),
|
||||
"D81" -> Ecopening("D81", "Grunfeld", "Grunfeld, Russian Variation", "d4 Nf6 c4 g6 Nc3 d5 Qb3", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/1QN5/PP2PPPP/R1B1KBNR", "d1b3"),
|
||||
"D82" -> Ecopening("D82", "Grunfeld", "Grunfeld, 4.Bf4", "d4 Nf6 c4 g6 Nc3 d5 Bf4", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP1B2/2N5/PP2PPPP/R2QKBNR", "c1f4"),
|
||||
"D83" -> Ecopening("D83", "Grunfeld", "Grunfeld, Grunfeld Gambit", "d4 Nf6 c4 g6 Nc3 d5 Bf4 Bg7 e3 O-O", "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP1B2/2N1P3/PP3PPP/R2QKBNR", "e8g8"),
|
||||
"D84" -> Ecopening("D84", "Grunfeld", "Grunfeld, Grunfeld Gambit Accepted", "d4 Nf6 c4 g6 Nc3 d5 Bf4 Bg7 e3 O-O cxd5 Nxd5 Nxd5 Qxd5 Bxc7", "rnb2rk1/ppB1ppbp/6p1/3q4/3P4/4P3/PP3PPP/R2QKBNR", "f4c7"),
|
||||
"D85" -> Ecopening("D85", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5", "rnbqkb1r/ppp1pp1p/6p1/3n4/3P4/2N5/PP2PPPP/R1BQKBNR", "f6d5"),
|
||||
"D86" -> Ecopening("D86", "Grunfeld", "Grunfeld, Exchange", "d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Bc4", "rnbqk2r/ppp1ppbp/6p1/8/2BPP3/2P5/P4PPP/R1BQK1NR", "f1c4"),
|
||||
"D87" -> Ecopening("D87", "Grunfeld", "Grunfeld, Exchange", "d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Bc4 O-O Ne2 c5", "rnbq1rk1/pp2ppbp/6p1/2p5/2BPP3/2P5/P3NPPP/R1BQK2R", "c7c5"),
|
||||
"D88" -> Ecopening("D88", "Grunfeld", "Grunfeld, Spassky Variation, Main line, 10...cd, 11.cd", "d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Bc4 O-O Ne2", "rnbq1rk1/ppp1ppbp/6p1/8/2BPP3/2P5/P3NPPP/R1BQK2R", "g1e2"),
|
||||
"D89" -> Ecopening("D89", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5 cxd5 Nxd5 e4 Nxc3 bxc3 Bg7 Bc4 O-O Ne2", "rnbq1rk1/ppp1ppbp/6p1/8/2BPP3/2P5/P3NPPP/R1BQK2R", "g1e2"),
|
||||
"D90" -> Ecopening("D90", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5 Nf3", "rnbqkb1r/ppp1pp1p/5np1/3p4/2PP4/2N2N2/PP2PPPP/R1BQKB1R", "g1f3"),
|
||||
"D91" -> Ecopening("D91", "Grunfeld", "Grunfeld, 5.Bg5", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Bg5", "rnbqk2r/ppp1ppbp/5np1/3p2B1/2PP4/2N2N2/PP2PPPP/R2QKB1R", "c1g5"),
|
||||
"D92" -> Ecopening("D92", "Grunfeld", "Grunfeld, 5.Bf4", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Bf4", "rnbqk2r/ppp1ppbp/5np1/3p4/2PP1B2/2N2N2/PP2PPPP/R2QKB1R", "c1f4"),
|
||||
"D93" -> Ecopening("D93", "Grunfeld", "Grunfeld, with Bf4 & e3", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Bf4 O-O e3", "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP1B2/2N1PN2/PP3PPP/R2QKB1R", "e2e3"),
|
||||
"D94" -> Ecopening("D94", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 e3", "rnbqk2r/ppp1ppbp/5np1/3p4/2PP4/2N1PN2/PP3PPP/R1BQKB1R", "e2e3"),
|
||||
"D95" -> Ecopening("D95", "Grunfeld", "Grunfeld", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 e3 O-O Qb3", "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP4/1QN1PN2/PP3PPP/R1B1KB1R", "d1b3"),
|
||||
"D96" -> Ecopening("D96", "Grunfeld", "Grunfeld, Russian Variation", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Qb3", "rnbqk2r/ppp1ppbp/5np1/3p4/2PP4/1QN2N2/PP2PPPP/R1B1KB1R", "d1b3"),
|
||||
"D97" -> Ecopening("D97", "Grunfeld", "Grunfeld, Russian", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Qb3 dxc4 Qxc4 O-O e4", "rnbq1rk1/ppp1ppbp/5np1/8/2QPP3/2N2N2/PP3PPP/R1B1KB1R", "e2e4"),
|
||||
"D98" -> Ecopening("D98", "Grunfeld", "Grunfeld, Russian", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Qb3 dxc4 Qxc4 O-O e4 Bg4", "rn1q1rk1/ppp1ppbp/5np1/8/2QPP1b1/2N2N2/PP3PPP/R1B1KB1R", "c8g4"),
|
||||
"D99" -> Ecopening("D99", "Grunfeld", "Grunfeld Defence, Smyslov", "d4 Nf6 c4 g6 Nc3 d5 Nf3 Bg7 Qb3 dxc4 Qxc4 O-O e4 Bg4 Be3", "rn1q1rk1/ppp1ppbp/5np1/8/2QPP1b1/2N1BN2/PP3PPP/R3KB1R", "c1e3"),
|
||||
"E00" -> Ecopening("E00", "Queen's Pawn Game", "Queen's Pawn Game", "d4 Nf6 c4 e6", "rnbqkb1r/pppp1ppp/4pn2/8/2PP4/8/PP2PPPP/RNBQKBNR", "e7e6"),
|
||||
"E01" -> Ecopening("E01", "Catalan", "Catalan, Closed", "d4 Nf6 c4 e6 g3 d5 Bg2", "rnbqkb1r/ppp2ppp/4pn2/3p4/2PP4/6P1/PP2PPBP/RNBQK1NR", "f1g2"),
|
||||
"E02" -> Ecopening("E02", "Catalan", "Catalan, Open, 5.Qa4", "d4 Nf6 c4 e6 g3 d5 Bg2 dxc4 Qa4+", "rnbqkb1r/ppp2ppp/4pn2/8/Q1pP4/6P1/PP2PPBP/RNB1K1NR", "d1a4"),
|
||||
"E03" -> Ecopening("E03", "Catalan", "Catalan, Open", "d4 Nf6 c4 e6 g3 d5 Bg2 dxc4 Qa4+ Nbd7 Qxc4", "r1bqkb1r/pppn1ppp/4pn2/8/2QP4/6P1/PP2PPBP/RNB1K1NR", "a4c4"),
|
||||
"E04" -> Ecopening("E04", "Catalan", "Catalan, Open, 5.Nf3", "d4 Nf6 c4 e6 g3 d5 Bg2 dxc4 Nf3", "rnbqkb1r/ppp2ppp/4pn2/8/2pP4/5NP1/PP2PPBP/RNBQK2R", "g1f3"),
|
||||
"E05" -> Ecopening("E05", "Catalan", "Catalan, Open, Classical line", "d4 Nf6 c4 e6 g3 d5 Bg2 dxc4 Nf3 Be7", "rnbqk2r/ppp1bppp/4pn2/8/2pP4/5NP1/PP2PPBP/RNBQK2R", "f8e7"),
|
||||
"E06" -> Ecopening("E06", "Catalan", "Catalan, Closed, 5.Nf3", "d4 Nf6 c4 e6 g3 d5 Bg2 Be7 Nf3", "rnbqk2r/ppp1bppp/4pn2/3p4/2PP4/5NP1/PP2PPBP/RNBQK2R", "g1f3"),
|
||||
"E07" -> Ecopening("E07", "Catalan", "Catalan, Closed", "d4 Nf6 c4 e6 g3 d5 Bg2 Be7 Nf3 O-O O-O Nbd7", "r1bq1rk1/pppnbppp/4pn2/3p4/2PP4/5NP1/PP2PPBP/RNBQ1RK1", "b8d7"),
|
||||
"E08" -> Ecopening("E08", "Catalan", "Catalan, Closed", "d4 Nf6 c4 e6 g3 d5 Bg2 Be7 Nf3 O-O O-O Nbd7 Qc2", "r1bq1rk1/pppnbppp/4pn2/3p4/2PP4/5NP1/PPQ1PPBP/RNB2RK1", "d1c2"),
|
||||
"E09" -> Ecopening("E09", "Catalan", "Catalan, Closed", "d4 Nf6 c4 e6 g3 d5 Bg2 Be7 Nf3 O-O O-O Nbd7 Qc2 c6 Nbd2", "r1bq1rk1/pp1nbppp/2p1pn2/3p4/2PP4/5NP1/PPQNPPBP/R1B2RK1", "b1d2"),
|
||||
"E10" -> Ecopening("E10", "Queen's Pawn Game", "Queen's Pawn Game", "d4 Nf6 c4 e6 Nf3", "rnbqkb1r/pppp1ppp/4pn2/8/2PP4/5N2/PP2PPPP/RNBQKB1R", "g1f3"),
|
||||
"E11" -> Ecopening("E11", "Bogo-Indian Defence", "Bogo-Indian Defence", "d4 Nf6 c4 e6 Nf3 Bb4+", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/5N2/PP2PPPP/RNBQKB1R", "f8b4"),
|
||||
"E12" -> Ecopening("E12", "Queen's Indian", "Queen's Indian", "d4 Nf6 c4 e6 Nf3 b6", "rnbqkb1r/p1pp1ppp/1p2pn2/8/2PP4/5N2/PP2PPPP/RNBQKB1R", "b7b6"),
|
||||
"E13" -> Ecopening("E13", "Queen's Indian", "Queen's Indian, 4.Nc3, Main line", "d4 Nf6 c4 e6 Nf3 b6 Nc3 Bb7 Bg5 h6 Bh4 Bb4", "rn1qk2r/pbpp1pp1/1p2pn1p/8/1bPP3B/2N2N2/PP2PPPP/R2QKB1R", "f8b4"),
|
||||
"E14" -> Ecopening("E14", "Queen's Indian", "Queen's Indian", "d4 Nf6 c4 e6 Nf3 b6 e3", "rnbqkb1r/p1pp1ppp/1p2pn2/8/2PP4/4PN2/PP3PPP/RNBQKB1R", "e2e3"),
|
||||
"E15" -> Ecopening("E15", "Queen's Indian", "Queen's Indian", "d4 Nf6 c4 e6 Nf3 b6 g3", "rnbqkb1r/p1pp1ppp/1p2pn2/8/2PP4/5NP1/PP2PP1P/RNBQKB1R", "g2g3"),
|
||||
"E16" -> Ecopening("E16", "Queen's Indian", "Queen's Indian", "d4 Nf6 c4 e6 Nf3 b6 g3 Bb7 Bg2 Bb4+", "rn1qk2r/pbpp1ppp/1p2pn2/8/1bPP4/5NP1/PP2PPBP/RNBQK2R", "f8b4"),
|
||||
"E17" -> Ecopening("E17", "Queen's Indian", "Queen's Indian", "d4 Nf6 c4 e6 Nf3 b6 g3 Bb7 Bg2 Be7", "rn1qk2r/pbppbppp/1p2pn2/8/2PP4/5NP1/PP2PPBP/RNBQK2R", "f8e7"),
|
||||
"E18" -> Ecopening("E18", "Queen's Indian", "Queen's Indian, Old Main line, 7.Nc3", "d4 Nf6 c4 e6 Nf3 b6 g3 Bb7 Bg2 Be7 O-O O-O Nc3", "rn1q1rk1/pbppbppp/1p2pn2/8/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "b1c3"),
|
||||
"E19" -> Ecopening("E19", "Queen's Indian", "Queen's Indian, Old Main line, 9.Qxc3", "d4 Nf6 c4 e6 Nf3 b6 g3 Bb7 Bg2 Be7 O-O O-O Nc3 Ne4 Qc2 Nxc3", "rn1q1rk1/pbppbppp/1p2p3/8/2PP4/2n2NP1/PPQ1PPBP/R1B2RK1", "e4c3"),
|
||||
"E20" -> Ecopening("E20", "Nimzo-Indian", "Nimzo-Indian", "d4 Nf6 c4 e6 Nc3 Bb4", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/2N5/PP2PPPP/R1BQKBNR", "f8b4"),
|
||||
"E21" -> Ecopening("E21", "Nimzo-Indian", "Nimzo-Indian, Three Knights", "d4 Nf6 c4 e6 Nc3 Bb4 Nf3", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/2N2N2/PP2PPPP/R1BQKB1R", "g1f3"),
|
||||
"E22" -> Ecopening("E22", "Nimzo-Indian", "Nimzo-Indian, Spielmann Variation", "d4 Nf6 c4 e6 Nc3 Bb4 Qb3", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/1QN5/PP2PPPP/R1B1KBNR", "d1b3"),
|
||||
"E23" -> Ecopening("E23", "Nimzo-Indian", "Nimzo-Indian, Spielmann", "d4 Nf6 c4 e6 Nc3 Bb4 Qb3 c5 dxc5 Nc6", "r1bqk2r/pp1p1ppp/2n1pn2/2P5/1bP5/1QN5/PP2PPPP/R1B1KBNR", "b8c6"),
|
||||
"E24" -> Ecopening("E24", "Nimzo-Indian", "Nimzo-Indian, Samisch", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3", "rnbqk2r/pppp1ppp/4pn2/8/2PP4/P1P5/4PPPP/R1BQKBNR", "b2c3"),
|
||||
"E25" -> Ecopening("E25", "Nimzo-Indian", "Nimzo-Indian, Samisch", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3 c5 f3 d5 cxd5", "rnbqk2r/pp3ppp/4pn2/2pP4/3P4/P1P2P2/4P1PP/R1BQKBNR", "c4d5"),
|
||||
"E26" -> Ecopening("E26", "Nimzo-Indian", "Nimzo-Indian, Samisch", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3 c5 e3", "rnbqk2r/pp1p1ppp/4pn2/2p5/2PP4/P1P1P3/5PPP/R1BQKBNR", "e2e3"),
|
||||
"E27" -> Ecopening("E27", "Nimzo-Indian", "Nimzo-Indian, Samisch Variation", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3 O-O", "rnbq1rk1/pppp1ppp/4pn2/8/2PP4/P1P5/4PPPP/R1BQKBNR", "e8g8"),
|
||||
"E28" -> Ecopening("E28", "Nimzo-Indian", "Nimzo-Indian, Samisch Variation", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3 O-O e3", "rnbq1rk1/pppp1ppp/4pn2/8/2PP4/P1P1P3/5PPP/R1BQKBNR", "e2e3"),
|
||||
"E29" -> Ecopening("E29", "Nimzo-Indian", "Nimzo-Indian, Samisch", "d4 Nf6 c4 e6 Nc3 Bb4 a3 Bxc3+ bxc3 O-O e3 c5 Bd3 Nc6", "r1bq1rk1/pp1p1ppp/2n1pn2/2p5/2PP4/P1PBP3/5PPP/R1BQK1NR", "b8c6"),
|
||||
"E30" -> Ecopening("E30", "Nimzo-Indian", "Nimzo-Indian, Leningrad", "d4 Nf6 c4 e6 Nc3 Bb4 Bg5", "rnbqk2r/pppp1ppp/4pn2/6B1/1bPP4/2N5/PP2PPPP/R2QKBNR", "c1g5"),
|
||||
"E31" -> Ecopening("E31", "Nimzo-Indian", "Nimzo-Indian, Leningrad, Main line", "d4 Nf6 c4 e6 Nc3 Bb4 Bg5 h6 Bh4 c5 d5 d6", "rnbqk2r/pp3pp1/3ppn1p/2pP4/1bP4B/2N5/PP2PPPP/R2QKBNR", "d7d6"),
|
||||
"E32" -> Ecopening("E32", "Nimzo-Indian", "Nimzo-Indian, Classical", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/2N5/PPQ1PPPP/R1B1KBNR", "d1c2"),
|
||||
"E33" -> Ecopening("E33", "Nimzo-Indian", "Nimzo-Indian, Classical", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 Nc6", "r1bqk2r/pppp1ppp/2n1pn2/8/1bPP4/2N5/PPQ1PPPP/R1B1KBNR", "b8c6"),
|
||||
"E34" -> Ecopening("E34", "Nimzo-Indian", "Nimzo-Indian, Classical, Noa Variation", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 d5", "rnbqk2r/ppp2ppp/4pn2/3p4/1bPP4/2N5/PPQ1PPPP/R1B1KBNR", "d7d5"),
|
||||
"E35" -> Ecopening("E35", "Nimzo-Indian", "Nimzo-Indian, Classical, Noa Variation, 5.cd ed", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 d5 cxd5 exd5", "rnbqk2r/ppp2ppp/5n2/3p4/1b1P4/2N5/PPQ1PPPP/R1B1KBNR", "e6d5"),
|
||||
"E36" -> Ecopening("E36", "Nimzo-Indian", "Nimzo-Indian, Classical", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 d5 a3", "rnbqk2r/ppp2ppp/4pn2/3p4/1bPP4/P1N5/1PQ1PPPP/R1B1KBNR", "a2a3"),
|
||||
"E37" -> Ecopening("E37", "Nimzo-Indian", "Nimzo-Indian, Classical", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 d5 a3 Bxc3+ Qxc3 Ne4 Qc2", "rnbqk2r/ppp2ppp/4p3/3p4/2PPn3/P7/1PQ1PPPP/R1B1KBNR", "c3c2"),
|
||||
"E38" -> Ecopening("E38", "Nimzo-Indian", "Nimzo-Indian, Classical, 4...c5", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 c5", "rnbqk2r/pp1p1ppp/4pn2/2p5/1bPP4/2N5/PPQ1PPPP/R1B1KBNR", "c7c5"),
|
||||
"E39" -> Ecopening("E39", "Nimzo-Indian", "Nimzo-Indian, Classical, Pirc Variation", "d4 Nf6 c4 e6 Nc3 Bb4 Qc2 c5 dxc5 O-O", "rnbq1rk1/pp1p1ppp/4pn2/2P5/1bP5/2N5/PPQ1PPPP/R1B1KBNR", "e8g8"),
|
||||
"E40" -> Ecopening("E40", "Nimzo-Indian", "Nimzo-Indian, 4.e3", "d4 Nf6 c4 e6 Nc3 Bb4 e3", "rnbqk2r/pppp1ppp/4pn2/8/1bPP4/2N1P3/PP3PPP/R1BQKBNR", "e2e3"),
|
||||
"E41" -> Ecopening("E41", "Nimzo-Indian", "Nimzo-Indian", "d4 Nf6 c4 e6 Nc3 Bb4 e3 c5", "rnbqk2r/pp1p1ppp/4pn2/2p5/1bPP4/2N1P3/PP3PPP/R1BQKBNR", "c7c5"),
|
||||
"E42" -> Ecopening("E42", "Nimzo-Indian", "Nimzo-Indian, 4.e3 c5, 5.Ne2 (Rubinstein)", "d4 Nf6 c4 e6 Nc3 Bb4 e3 c5 Ne2", "rnbqk2r/pp1p1ppp/4pn2/2p5/1bPP4/2N1P3/PP2NPPP/R1BQKB1R", "g1e2"),
|
||||
"E43" -> Ecopening("E43", "Nimzo-Indian", "Nimzo-Indian, Fischer Variation", "d4 Nf6 c4 e6 Nc3 Bb4 e3 b6", "rnbqk2r/p1pp1ppp/1p2pn2/8/1bPP4/2N1P3/PP3PPP/R1BQKBNR", "b7b6"),
|
||||
"E44" -> Ecopening("E44", "Nimzo-Indian", "Nimzo-Indian, Fischer Variation, 5.Ne2", "d4 Nf6 c4 e6 Nc3 Bb4 e3 b6 Ne2", "rnbqk2r/p1pp1ppp/1p2pn2/8/1bPP4/2N1P3/PP2NPPP/R1BQKB1R", "g1e2"),
|
||||
"E45" -> Ecopening("E45", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Bronstein (Byrne) Variation", "d4 Nf6 c4 e6 Nc3 Bb4 e3 b6 Ne2 Ba6", "rn1qk2r/p1pp1ppp/bp2pn2/8/1bPP4/2N1P3/PP2NPPP/R1BQKB1R", "c8a6"),
|
||||
"E46" -> Ecopening("E46", "Nimzo-Indian", "Nimzo-Indian", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O", "rnbq1rk1/pppp1ppp/4pn2/8/1bPP4/2N1P3/PP3PPP/R1BQKBNR", "e8g8"),
|
||||
"E47" -> Ecopening("E47", "Nimzo-Indian", "Nimzo-Indian, 4.e3 O-O 5.Bd3", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Bd3", "rnbq1rk1/pppp1ppp/4pn2/8/1bPP4/2NBP3/PP3PPP/R1BQK1NR", "f1d3"),
|
||||
"E48" -> Ecopening("E48", "Nimzo-Indian", "Nimzo-Indian, 4.e3 O-O 5.Bd3 d5", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Bd3 d5", "rnbq1rk1/ppp2ppp/4pn2/3p4/1bPP4/2NBP3/PP3PPP/R1BQK1NR", "d7d5"),
|
||||
"E49" -> Ecopening("E49", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Botvinnik System", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Bd3 d5 a3 Bxc3+ bxc3", "rnbq1rk1/ppp2ppp/4pn2/3p4/2PP4/P1PBP3/5PPP/R1BQK1NR", "b2c3"),
|
||||
"E50" -> Ecopening("E50", "Nimzo-Indian", "Nimzo-Indian, 4.e3 O-O 5.Nf3, without ...d5", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3", "rnbq1rk1/pppp1ppp/4pn2/8/1bPP4/2N1PN2/PP3PPP/R1BQKB1R", "g1f3"),
|
||||
"E51" -> Ecopening("E51", "Nimzo-Indian", "Nimzo-Indian, 4.e3", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5", "rnbq1rk1/ppp2ppp/4pn2/3p4/1bPP4/2N1PN2/PP3PPP/R1BQKB1R", "d7d5"),
|
||||
"E52" -> Ecopening("E52", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Main line with ...b6", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 b6", "rnbq1rk1/p1p2ppp/1p2pn2/3p4/1bPP4/2NBPN2/PP3PPP/R1BQK2R", "b7b6"),
|
||||
"E53" -> Ecopening("E53", "Nimzo-Indian", "Nimzo-Indian, 4.e3", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5", "rnbq1rk1/pp3ppp/4pn2/2pp4/1bPP4/2NBPN2/PP3PPP/R1BQK2R", "c7c5"),
|
||||
"E54" -> Ecopening("E54", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Gligoric System", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O dxc4 Bxc4", "rnbq1rk1/pp3ppp/4pn2/2p5/1bBP4/2N1PN2/PP3PPP/R1BQ1RK1", "d3c4"),
|
||||
"E55" -> Ecopening("E55", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Gligoric System, Bronstein Variation", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O dxc4 Bxc4 Nbd7", "r1bq1rk1/pp1n1ppp/4pn2/2p5/1bBP4/2N1PN2/PP3PPP/R1BQ1RK1", "b8d7"),
|
||||
"E56" -> Ecopening("E56", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Main line with 7...Nc6", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O Nc6", "r1bq1rk1/pp3ppp/2n1pn2/2pp4/1bPP4/2NBPN2/PP3PPP/R1BQ1RK1", "b8c6"),
|
||||
"E57" -> Ecopening("E57", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Main line with 8...dc and 9...cd", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O Nc6 a3 dxc4 Bxc4", "r1bq1rk1/pp3ppp/2n1pn2/2p5/1bBP4/P1N1PN2/1P3PPP/R1BQ1RK1", "d3c4"),
|
||||
"E58" -> Ecopening("E58", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Main line with 8...Bxc3", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O Nc6 a3 Bxc3 bxc3", "r1bq1rk1/pp3ppp/2n1pn2/2pp4/2PP4/P1PBPN2/5PPP/R1BQ1RK1", "b2c3"),
|
||||
"E59" -> Ecopening("E59", "Nimzo-Indian", "Nimzo-Indian, 4.e3, Main line", "d4 Nf6 c4 e6 Nc3 Bb4 e3 O-O Nf3 d5 Bd3 c5 O-O Nc6 a3 Bxc3 bxc3 dxc4 Bxc4", "r1bq1rk1/pp3ppp/2n1pn2/2p5/2BP4/P1P1PN2/5PPP/R1BQ1RK1", "d3c4"),
|
||||
"E60" -> Ecopening("E60", "King's Indian", "King's Indian Defence", "d4 Nf6 c4 g6", "rnbqkb1r/pppppp1p/5np1/8/2PP4/8/PP2PPPP/RNBQKBNR", "g7g6"),
|
||||
"E61" -> Ecopening("E61", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3", "rnbqkb1r/pppppp1p/5np1/8/2PP4/2N5/PP2PPPP/R1BQKBNR", "b1c3"),
|
||||
"E62" -> Ecopening("E62", "King's Indian", "King's Indian, Fianchetto", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3", "rnbqk2r/ppp1ppbp/3p1np1/8/2PP4/2N2NP1/PP2PP1P/R1BQKB1R", "g2g3"),
|
||||
"E63" -> Ecopening("E63", "King's Indian", "King's Indian, Fianchetto, Panno Variation", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 Nc6 O-O a6", "r1bq1rk1/1pp1ppbp/p1np1np1/8/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "a7a6"),
|
||||
"E64" -> Ecopening("E64", "King's Indian", "King's Indian, Fianchetto, Yugoslav System", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 c5", "rnbq1rk1/pp2ppbp/3p1np1/2p5/2PP4/2N2NP1/PP2PPBP/R1BQK2R", "c7c5"),
|
||||
"E65" -> Ecopening("E65", "King's Indian", "King's Indian, Fianchetto, Yugoslav, 7.O-O", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 c5 O-O", "rnbq1rk1/pp2ppbp/3p1np1/2p5/2PP4/2N2NP1/PP2PPBP/R1BQ1RK1", "e1g1"),
|
||||
"E66" -> Ecopening("E66", "King's Indian", "King's Indian, Fianchetto, Yugoslav Panno", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 c5 O-O Nc6 d5", "r1bq1rk1/pp2ppbp/2np1np1/2pP4/2P5/2N2NP1/PP2PPBP/R1BQ1RK1", "d4d5"),
|
||||
"E67" -> Ecopening("E67", "King's Indian", "King's Indian, Fianchetto", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 Nbd7", "r1bq1rk1/pppnppbp/3p1np1/8/2PP4/2N2NP1/PP2PPBP/R1BQK2R", "b8d7"),
|
||||
"E68" -> Ecopening("E68", "King's Indian", "King's Indian, Fianchetto, Classical Variation, 8.e4", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 Nbd7 O-O e5 e4", "r1bq1rk1/pppn1pbp/3p1np1/4p3/2PPP3/2N2NP1/PP3PBP/R1BQ1RK1", "e2e4"),
|
||||
"E69" -> Ecopening("E69", "King's Indian", "King's Indian, Fianchetto, Classical Main line", "d4 Nf6 c4 g6 Nc3 Bg7 Nf3 d6 g3 O-O Bg2 Nbd7 O-O e5 e4 c6 h3", "r1bq1rk1/pp1n1pbp/2pp1np1/4p3/2PPP3/2N2NPP/PP3PB1/R1BQ1RK1", "h2h3"),
|
||||
"E70" -> Ecopening("E70", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4", "rnbqk2r/ppppppbp/5np1/8/2PPP3/2N5/PP3PPP/R1BQKBNR", "e2e4"),
|
||||
"E71" -> Ecopening("E71", "King's Indian", "King's Indian, Makagonov System (5.h3)", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 h3", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPP3/2N4P/PP3PP1/R1BQKBNR", "h2h3"),
|
||||
"E72" -> Ecopening("E72", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 g3", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPP3/2N3P1/PP3P1P/R1BQKBNR", "g2g3"),
|
||||
"E73" -> Ecopening("E73", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Be2", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPP3/2N5/PP2BPPP/R1BQK1NR", "f1e2"),
|
||||
"E74" -> Ecopening("E74", "King's Indian", "King's Indian, Averbakh, 6...c5", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Be2 O-O Bg5 c5", "rnbq1rk1/pp2ppbp/3p1np1/2p3B1/2PPP3/2N5/PP2BPPP/R2QK1NR", "c7c5"),
|
||||
"E75" -> Ecopening("E75", "King's Indian", "King's Indian, Averbakh, Main line", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Be2 O-O Bg5 c5 d5 e6", "rnbq1rk1/pp3pbp/3ppnp1/2pP2B1/2P1P3/2N5/PP2BPPP/R2QK1NR", "e7e6"),
|
||||
"E76" -> Ecopening("E76", "King's Indian", "King's Indian, Four Pawns Attack", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f4", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPPP2/2N5/PP4PP/R1BQKBNR", "f2f4"),
|
||||
"E77" -> Ecopening("E77", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f4 O-O Be2", "rnbq1rk1/ppp1ppbp/3p1np1/8/2PPPP2/2N5/PP2B1PP/R1BQK1NR", "f1e2"),
|
||||
"E78" -> Ecopening("E78", "King's Indian", "King's Indian, Four Pawns Attack, with Be2 and Nf3", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f4 O-O Be2 c5 Nf3", "rnbq1rk1/pp2ppbp/3p1np1/2p5/2PPPP2/2N2N2/PP2B1PP/R1BQK2R", "g1f3"),
|
||||
"E79" -> Ecopening("E79", "King's Indian", "King's Indian, Four Pawns Attack, Main line", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f4 O-O Be2 c5 Nf3 cxd4 Nxd4 Nc6 Be3", "r1bq1rk1/pp2ppbp/2np1np1/8/2PNPP2/2N1B3/PP2B1PP/R2QK2R", "c1e3"),
|
||||
"E80" -> Ecopening("E80", "King's Indian", "King's Indian, Samisch Variation", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPP3/2N2P2/PP4PP/R1BQKBNR", "f2f3"),
|
||||
"E81" -> Ecopening("E81", "King's Indian", "King's Indian, Samisch", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O", "rnbq1rk1/ppp1ppbp/3p1np1/8/2PPP3/2N2P2/PP4PP/R1BQKBNR", "e8g8"),
|
||||
"E82" -> Ecopening("E82", "King's Indian", "King's Indian, Samisch, double Fianchetto Variation", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 b6", "rnbq1rk1/p1p1ppbp/1p1p1np1/8/2PPP3/2N1BP2/PP4PP/R2QKBNR", "b7b6"),
|
||||
"E83" -> Ecopening("E83", "King's Indian", "King's Indian, Samisch", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 Nc6", "r1bq1rk1/ppp1ppbp/2np1np1/8/2PPP3/2N1BP2/PP4PP/R2QKBNR", "b8c6"),
|
||||
"E84" -> Ecopening("E84", "King's Indian", "King's Indian, Samisch, Panno Main line", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 Nc6 Nge2 a6 Qd2 Rb8", "1rbq1rk1/1pp1ppbp/p1np1np1/8/2PPP3/2N1BP2/PP1QN1PP/R3KB1R", "a8b8"),
|
||||
"E85" -> Ecopening("E85", "King's Indian", "King's Indian, Samisch, Orthodox Variation", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 e5", "rnbq1rk1/ppp2pbp/3p1np1/4p3/2PPP3/2N1BP2/PP4PP/R2QKBNR", "e7e5"),
|
||||
"E86" -> Ecopening("E86", "King's Indian", "King's Indian, Samisch, Orthodox, 7.Nge2 c6", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 e5 Nge2 c6", "rnbq1rk1/pp3pbp/2pp1np1/4p3/2PPP3/2N1BP2/PP2N1PP/R2QKB1R", "c7c6"),
|
||||
"E87" -> Ecopening("E87", "King's Indian", "King's Indian, Samisch, Orthodox", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 e5 d5", "rnbq1rk1/ppp2pbp/3p1np1/3Pp3/2P1P3/2N1BP2/PP4PP/R2QKBNR", "d4d5"),
|
||||
"E88" -> Ecopening("E88", "King's Indian", "King's Indian, Samisch, Orthodox, 7.d5 c6", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 e5 d5 c6", "rnbq1rk1/pp3pbp/2pp1np1/3Pp3/2P1P3/2N1BP2/PP4PP/R2QKBNR", "c7c6"),
|
||||
"E89" -> Ecopening("E89", "King's Indian", "King's Indian, Samisch, Orthodox Main line", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 f3 O-O Be3 e5 d5 c6 Nge2 cxd5", "rnbq1rk1/pp3pbp/3p1np1/3pp3/2P1P3/2N1BP2/PP2N1PP/R2QKB1R", "c6d5"),
|
||||
"E90" -> Ecopening("E90", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3", "rnbqk2r/ppp1ppbp/3p1np1/8/2PPP3/2N2N2/PP3PPP/R1BQKB1R", "g1f3"),
|
||||
"E91" -> Ecopening("E91", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2", "rnbq1rk1/ppp1ppbp/3p1np1/8/2PPP3/2N2N2/PP2BPPP/R1BQK2R", "f1e2"),
|
||||
"E92" -> Ecopening("E92", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5", "rnbq1rk1/ppp2pbp/3p1np1/4p3/2PPP3/2N2N2/PP2BPPP/R1BQK2R", "e7e5"),
|
||||
"E93" -> Ecopening("E93", "King's Indian", "King's Indian, Petrosian System", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 d5 Nbd7", "r1bq1rk1/pppn1pbp/3p1np1/3Pp3/2P1P3/2N2N2/PP2BPPP/R1BQK2R", "b8d7"),
|
||||
"E94" -> Ecopening("E94", "King's Indian", "King's Indian, Orthodox", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O", "rnbq1rk1/ppp2pbp/3p1np1/4p3/2PPP3/2N2N2/PP2BPPP/R1BQ1RK1", "e1g1"),
|
||||
"E95" -> Ecopening("E95", "King's Indian", "King's Indian, Orthodox, 7...Nbd7, 8.Re1", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nbd7 Re1", "r1bq1rk1/pppn1pbp/3p1np1/4p3/2PPP3/2N2N2/PP2BPPP/R1BQR1K1", "f1e1"),
|
||||
"E96" -> Ecopening("E96", "King's Indian", "King's Indian, Orthodox, 7...Nbd7, Main line", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nbd7 Re1 c6 Bf1 a5", "r1bq1rk1/1p1n1pbp/2pp1np1/p3p3/2PPP3/2N2N2/PP3PPP/R1BQRBK1", "a7a5"),
|
||||
"E97" -> Ecopening("E97", "King's Indian", "King's Indian", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nc6", "r1bq1rk1/ppp2pbp/2np1np1/4p3/2PPP3/2N2N2/PP2BPPP/R1BQ1RK1", "b8c6"),
|
||||
"E98" -> Ecopening("E98", "King's Indian", "King's Indian, Orthodox, Taimanov, 9.Ne1", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nc6 d5 Ne7 Ne1", "r1bq1rk1/ppp1npbp/3p1np1/3Pp3/2P1P3/2N5/PP2BPPP/R1BQNRK1", "f3e1"),
|
||||
"E99" -> Ecopening("E99", "King's Indian", "King's Indian, Orthodox, Taimanov", "d4 Nf6 c4 g6 Nc3 Bg7 e4 d6 Nf3 O-O Be2 e5 O-O Nc6 d5 Ne7 Ne1 Nd7 f3 f5", "r1bq1rk1/pppnn1bp/3p2p1/3Ppp2/2P1P3/2N2P2/PP2B1PP/R1BQNRK1", "f7f5"))
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import akka.actor._
|
||||
import com.typesafe.config.Config
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import akka.actor._
|
||||
import lila.common.PimpedConfig._
|
||||
|
||||
final class Env(
|
||||
config: Config,
|
||||
getPref: String => Fu[lila.pref.Pref],
|
||||
areFriends: (String, String) => Fu[Boolean],
|
||||
lightUser: String => Option[lila.common.LightUser],
|
||||
system: ActorSystem,
|
||||
db: lila.db.Env) {
|
||||
|
||||
private val settings = new {
|
||||
val CollectionStat = config getString "collection.stat"
|
||||
}
|
||||
import settings._
|
||||
|
||||
private lazy val jsonWriters = new JSONWriters(lightUser = lightUser)
|
||||
|
||||
lazy val share = new Share(getPref, areFriends)
|
||||
|
||||
lazy val jsonView = new JsonView(jsonWriters)
|
||||
|
||||
lazy val statApi = new StatApi(
|
||||
coll = db(CollectionStat))
|
||||
|
||||
lazy val aggregator = new Aggregator(
|
||||
api = statApi,
|
||||
sequencer = system.actorOf(Props(classOf[lila.hub.Sequencer], None)))
|
||||
}
|
||||
|
||||
object Env {
|
||||
|
||||
lazy val current: Env = "coach" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "coach",
|
||||
getPref = lila.pref.Env.current.api.getPrefById,
|
||||
areFriends = lila.relation.Env.current.api.areFriends,
|
||||
lightUser = lila.user.Env.current.lightUser,
|
||||
system = lila.common.PlayApp.system,
|
||||
db = lila.db.Env.current)
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
case class GameSections(
|
||||
all: GameSections.Section,
|
||||
opening: GameSections.Section,
|
||||
middle: GameSections.Section,
|
||||
end: GameSections.Section) {
|
||||
|
||||
private def avg(ints: Vector[Int]) = ints.nonEmpty option (ints.sum / ints.size)
|
||||
private def boundedAvg(ints: Option[Vector[Int]], bounds: (Int, Int)) = ints ?? { is =>
|
||||
bounds match {
|
||||
case (from, to) => avg(is drop from take (to - from))
|
||||
}
|
||||
}
|
||||
|
||||
def aggregate(p: RichPov) = {
|
||||
val moveTimes = p.pov.game.hasClock option p.pov.game.moveTimes(p.pov.color).toVector
|
||||
copy(
|
||||
all = all.add(
|
||||
m = p.division.plies / 2,
|
||||
a = p.accuracy.map(_.all),
|
||||
t = moveTimes.flatMap(avg)),
|
||||
opening = opening.add(
|
||||
m = p.division.openingSize / 2,
|
||||
a = p.accuracy.map(_.opening),
|
||||
t = p.division.openingBounds.flatMap { boundedAvg(moveTimes, _) }),
|
||||
middle = middle.add(
|
||||
m = ~p.division.middleSize / 2,
|
||||
a = p.accuracy.flatMap(_.middle),
|
||||
t = p.division.middleBounds.flatMap { boundedAvg(moveTimes, _) }),
|
||||
end = end.add(
|
||||
m = ~p.division.endSize / 2,
|
||||
a = p.accuracy.flatMap(_.end),
|
||||
t = p.division.endBounds.flatMap { boundedAvg(moveTimes, _) })
|
||||
)
|
||||
}
|
||||
|
||||
def merge(s: GameSections) = GameSections(
|
||||
all = all merge s.all,
|
||||
opening = opening merge s.opening,
|
||||
middle = middle merge s.middle,
|
||||
end = end merge s.end)
|
||||
}
|
||||
|
||||
object GameSections {
|
||||
|
||||
case class Section(
|
||||
nb: Int,
|
||||
moves: NbSum,
|
||||
acpl: NbSum,
|
||||
time: NbSum) {
|
||||
|
||||
def merge(m: Section) = Section(
|
||||
nb = nb + m.nb,
|
||||
moves = moves merge m.moves,
|
||||
acpl = acpl merge m.acpl,
|
||||
time = time merge m.time)
|
||||
|
||||
def add(m: Int, a: Option[Int], t: Option[Int]) = copy(
|
||||
nb = nb + 1,
|
||||
moves = moves add m,
|
||||
acpl = a.fold(acpl)(acpl.add),
|
||||
time = t.fold(time)(time.add))
|
||||
}
|
||||
object Section {
|
||||
val empty = Section(0, NbSum.empty, NbSum.empty, NbSum.empty)
|
||||
}
|
||||
|
||||
val empty = GameSections(Section.empty, Section.empty, Section.empty, Section.empty)
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.ISODateTimeFormat
|
||||
import play.api.libs.json._
|
||||
|
||||
private[coach] final class JSONWriters(
|
||||
lightUser: String => Option[lila.common.LightUser]) {
|
||||
|
||||
implicit object JodaDateWrites extends Writes[DateTime] {
|
||||
private val isoFormatter = ISODateTimeFormat.dateTime
|
||||
def writes(d: DateTime): JsValue = JsString(isoFormatter print d)
|
||||
}
|
||||
|
||||
implicit val NbSumWriter = OWrites[NbSum] { s =>
|
||||
Json.obj("nb" -> s.nb, "avg" -> s.avg)
|
||||
}
|
||||
|
||||
private val AutoEcopeningWriter = Json.writes[Ecopening]
|
||||
implicit val EcopeningWriter = OWrites[Ecopening] { o =>
|
||||
AutoEcopeningWriter.writes(o) + ("formattedMoves" -> JsString(o.formattedMoves))
|
||||
}
|
||||
implicit val SectionWriter = Json.writes[GameSections.Section]
|
||||
implicit val GameSectionsWriter = Json.writes[GameSections]
|
||||
implicit val BestWinWriter = OWrites[Results.BestWin] { o =>
|
||||
Json.obj(
|
||||
"id" -> o.id,
|
||||
"rating" -> o.rating,
|
||||
"user" -> lightUser(o.userId).map { u =>
|
||||
Json.obj(
|
||||
"id" -> u.id,
|
||||
"name" -> u.name,
|
||||
"title" -> u.title)
|
||||
})
|
||||
}
|
||||
implicit val MoveWriter = Json.writes[Move]
|
||||
implicit val TrimmedMovesWriter = Writes[TrimmedMoves] { o =>
|
||||
Json.toJson(o.moves)
|
||||
}
|
||||
implicit val ColorMovesWriter = Json.writes[ColorMoves]
|
||||
implicit val ResultsWriter = OWrites[Results] { o =>
|
||||
Json.obj(
|
||||
"nbGames" -> o.nbGames,
|
||||
"nbAnalysis" -> o.nbAnalysis,
|
||||
"nbWin" -> o.nbWin,
|
||||
"nbLoss" -> o.nbLoss,
|
||||
"nbDraw" -> o.nbDraw,
|
||||
"ratingDiff" -> o.ratingDiff,
|
||||
"gameSections" -> o.gameSections,
|
||||
"bestWin" -> o.bestWin,
|
||||
"opponentRatingAvg" -> o.opponentRatingAvg,
|
||||
"lastPlayed" -> o.lastPlayed)
|
||||
}
|
||||
implicit val ColorResultsWriter = Json.writes[ColorResults]
|
||||
implicit val OpeningsMapWriter = Writes[Openings.OpeningsMap] { o =>
|
||||
Json.obj(
|
||||
"map" -> Json.toJson(o.trim.m),
|
||||
"results" -> o.results
|
||||
)
|
||||
}
|
||||
implicit val PerfTypeWriter = Writes[lila.rating.PerfType] { o =>
|
||||
Json.obj(
|
||||
"key" -> o.key,
|
||||
"name" -> o.name,
|
||||
"icon" -> o.iconChar.toString)
|
||||
}
|
||||
implicit val OpeningsWriter = Json.writes[Openings]
|
||||
implicit val PerfResultsBestRatingWriter = Json.writes[PerfResults.BestRating]
|
||||
implicit val PerfResultsStatusMapWriter = OWrites[Map[chess.Status, Int]] { m =>
|
||||
JsObject(m.map { case (status, i) => status.name -> JsNumber(i) })
|
||||
}
|
||||
implicit val PerfResultsStatusScoresWriter = Json.writes[PerfResults.StatusScores]
|
||||
implicit val PerfResultsOutcomeStatusesWriter = Json.writes[PerfResults.OutcomeStatuses]
|
||||
implicit val PerfResultsWriter = Json.writes[PerfResults]
|
||||
implicit val PerfResultsPerfMapWriter = OWrites[Map[lila.rating.PerfType, PerfResults]] { m =>
|
||||
JsObject(m.map { case (pt, res) => pt.key -> PerfResultsWriter.writes(res) })
|
||||
}
|
||||
implicit val PerfResultsMapWriter = Json.writes[PerfResults.PerfResultsMap]
|
||||
implicit val UserStatWriter = Json.writes[UserStat]
|
||||
|
||||
implicit val OpeningWriter: OWrites[chess.Opening] = OWrites { o =>
|
||||
Json.obj(
|
||||
"code" -> o.code,
|
||||
"name" -> o.name,
|
||||
"moves" -> o.moves
|
||||
)
|
||||
}
|
||||
|
||||
implicit val EcopeningFamilyWriter = Json.writes[Ecopening.Family]
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import play.api.libs.json._
|
||||
|
||||
final class JsonView(jsonWriters: JSONWriters) {
|
||||
|
||||
import jsonWriters._
|
||||
|
||||
def opening(period: Period, color: chess.Color): Fu[JsObject] = fuccess {
|
||||
val stat = period.data
|
||||
val openings = stat.openings(color).trim
|
||||
Json.obj(
|
||||
"from" -> period.from,
|
||||
"to" -> period.to,
|
||||
"color" -> color.name,
|
||||
"results" -> stat.results.base,
|
||||
"colorResults" -> stat.colorResults(color),
|
||||
"openings" -> openings.m.map {
|
||||
case (eco, results) => eco -> Json.obj(
|
||||
"opening" -> EcopeningDB.allByEco.get(eco),
|
||||
"results" -> results)
|
||||
},
|
||||
"openingResults" -> openings.results,
|
||||
"families" -> Ecopening.makeFamilies {
|
||||
openings.m.keys.flatMap(EcopeningDB.allByEco.get)
|
||||
}.values.toList.sortBy(-_.ecos.size).map { fam =>
|
||||
Json.obj(
|
||||
"family" -> fam,
|
||||
"results" -> fam.ecos.flatMap(openings.m.get).foldLeft(Results.empty) {
|
||||
(res, oRes) => res merge oRes
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def move(period: Period): Fu[JsObject] = fuccess {
|
||||
val stat = period.data
|
||||
Json.obj(
|
||||
"from" -> period.from,
|
||||
"to" -> period.to,
|
||||
"perfs" -> (Json.obj(
|
||||
"perf" -> Json.obj(
|
||||
"key" -> "global",
|
||||
"name" -> "Global",
|
||||
"icon" -> "C"),
|
||||
"results" -> stat.results
|
||||
) :: lila.rating.PerfType.nonPuzzle.flatMap { pt =>
|
||||
stat.perfResults.m.get(pt) map { results =>
|
||||
Json.obj(
|
||||
"perf" -> pt,
|
||||
"results" -> results)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
case class Move(nb: Int, acpl: NbSum, time: NbSum) {
|
||||
|
||||
def merge(m: Move) = Move(
|
||||
nb = nb + m.nb,
|
||||
acpl = acpl merge m.acpl,
|
||||
time = time merge m.time)
|
||||
|
||||
def add(a: Option[Int], t: Option[Int]) = copy(
|
||||
nb = nb + 1,
|
||||
acpl = a.fold(acpl)(acpl.add),
|
||||
time = t.fold(time)(time.add))
|
||||
}
|
||||
object Move {
|
||||
val empty = Move(0, NbSum.empty, NbSum.empty)
|
||||
}
|
||||
|
||||
case class TrimmedMoves(moves: Vector[Move]) {
|
||||
|
||||
def fill = FilledMoves {
|
||||
moves ++ Vector.fill(Moves.SIZE - moves.size)(Move.empty)
|
||||
}
|
||||
|
||||
def aggregate(p: RichPov) = fill.aggregate(p).trim
|
||||
|
||||
def merge(o: TrimmedMoves) = fill.merge(o.fill).trim
|
||||
}
|
||||
|
||||
case class FilledMoves(moves: Vector[Move]) {
|
||||
|
||||
def trim = TrimmedMoves {
|
||||
moves.takeWhile(_.nb > 0)
|
||||
}
|
||||
|
||||
def aggregate(p: RichPov) = FilledMoves {
|
||||
val moveTimes = p.pov.game.hasClock option p.pov.game.moveTimes(p.pov.color).toVector
|
||||
val accuracy = p.moveAccuracy.map(_.toVector)
|
||||
|
||||
(0 to (Moves.SIZE - 1).min(p.pov.game.playerMoves(p.pov.color) - 1)).foldLeft(moves) {
|
||||
case (moves, index) => moves.updated(
|
||||
index,
|
||||
moves(index).add(
|
||||
accuracy.flatMap(_ lift index),
|
||||
if (index == 0) Some(0) else moveTimes.flatMap(_ lift index)))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO keep longest size
|
||||
def merge(o: FilledMoves) = FilledMoves {
|
||||
moves zip o.moves map { case (a, b) => a merge b }
|
||||
}
|
||||
}
|
||||
object Moves {
|
||||
val SIZE = 60
|
||||
val empty = TrimmedMoves(Vector.empty)
|
||||
}
|
||||
|
||||
case class ColorMoves(white: TrimmedMoves, black: TrimmedMoves) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
white = if (p.pov.color.white) white aggregate p else white,
|
||||
black = if (p.pov.color.black) black aggregate p else black)
|
||||
|
||||
def merge(o: ColorMoves) = ColorMoves(
|
||||
white = white merge o.white,
|
||||
black = black merge o.black)
|
||||
}
|
||||
|
||||
object ColorMoves {
|
||||
|
||||
val empty = ColorMoves(Moves.empty, Moves.empty)
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import chess.Color
|
||||
import lila.analyse.Analysis
|
||||
import lila.game.Pov
|
||||
|
||||
case class Openings(
|
||||
white: Openings.OpeningsMap,
|
||||
black: Openings.OpeningsMap) {
|
||||
|
||||
def apply(c: Color) = c.fold(white, black)
|
||||
|
||||
def merge(o: Openings) = Openings(
|
||||
white = white merge o.white,
|
||||
black = black merge o.black)
|
||||
}
|
||||
|
||||
object Openings {
|
||||
|
||||
val MAP_SIZE = 15
|
||||
|
||||
case class OpeningsMap(m: Map[String, Results]) {
|
||||
def best: List[(String, Results)] = m.toList.sortBy(-_._2.nbGames) take MAP_SIZE
|
||||
def trim = OpeningsMap(m = best.toMap)
|
||||
def merge(o: OpeningsMap) = OpeningsMap {
|
||||
m.map {
|
||||
case (k, v) => k -> o.m.get(k).fold(v)(v.merge)
|
||||
} ++ o.m.filterKeys(k => !m.contains(k))
|
||||
}
|
||||
lazy val results = m.foldLeft(Results.empty) {
|
||||
case (res, (_, r)) => res merge r
|
||||
}
|
||||
}
|
||||
val emptyOpeningsMap = OpeningsMap(Map.empty)
|
||||
|
||||
val empty = Openings(emptyOpeningsMap, emptyOpeningsMap)
|
||||
|
||||
case class Computation(
|
||||
white: Map[String, Results.Computation],
|
||||
black: Map[String, Results.Computation]) {
|
||||
|
||||
def aggregate(p: RichPov) =
|
||||
Ecopening.fromGame(p.pov.game).map(_.eco).fold(this) { eco =>
|
||||
copy(
|
||||
white = if (p.pov.color.white) agg(white, eco, p) else white,
|
||||
black = if (p.pov.color.black) agg(black, eco, p) else black)
|
||||
}
|
||||
|
||||
private def agg(ops: Map[String, Results.Computation], eco: String, p: RichPov) =
|
||||
ops + (eco -> ops.get(eco).|(Results.emptyComputation).aggregate(p))
|
||||
|
||||
def run = Openings(
|
||||
white = OpeningsMap(white.mapValues(_.run)).trim,
|
||||
black = OpeningsMap(black.mapValues(_.run)).trim)
|
||||
}
|
||||
val emptyComputation = Computation(Map.empty, Map.empty)
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import chess.Status
|
||||
import lila.game.Pov
|
||||
import lila.rating.PerfType
|
||||
|
||||
case class PerfResults(
|
||||
base: Results,
|
||||
moves: ColorMoves,
|
||||
bestRating: Option[PerfResults.BestRating],
|
||||
// winStreak: PerfResults.Streak, // nb games won in a row
|
||||
// awakeMinutesStreak: PerfResults.Streak, // minutes played without sleeping
|
||||
// dayStreak: PerfResults.Streak, // days played in a row
|
||||
outcomeStatuses: PerfResults.OutcomeStatuses) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
base = base aggregate p,
|
||||
moves = moves aggregate p,
|
||||
bestRating = if (~p.pov.win) {
|
||||
PerfResults.makeBestRating(p.pov).fold(bestRating) { newBest =>
|
||||
bestRating.fold(newBest) { prev =>
|
||||
if (newBest.rating > prev.rating) newBest else prev
|
||||
}.some
|
||||
}
|
||||
}
|
||||
else bestRating,
|
||||
outcomeStatuses = outcomeStatuses.aggregate(p))
|
||||
|
||||
def merge(o: PerfResults) = PerfResults(
|
||||
base = base merge o.base,
|
||||
moves = moves merge o.moves,
|
||||
bestRating = (bestRating, o.bestRating) match {
|
||||
case (Some(a), Some(b)) => Some(a merge b)
|
||||
case (a, b) => a orElse b
|
||||
},
|
||||
outcomeStatuses = outcomeStatuses merge o.outcomeStatuses)
|
||||
}
|
||||
|
||||
object PerfResults {
|
||||
|
||||
case class BestRating(id: String, userId: String, rating: Int) {
|
||||
def merge(o: BestRating) = if (rating >= o.rating) this else o
|
||||
}
|
||||
def makeBestRating(pov: Pov): Option[BestRating] =
|
||||
pov.opponent.userId |@| pov.player.ratingAfter apply {
|
||||
case (opId, myRating) => BestRating(pov.gameId, opId, myRating)
|
||||
}
|
||||
|
||||
case class PerfResultsMap(m: Map[PerfType, PerfResults]) {
|
||||
def sorted: List[(PerfType, PerfResults)] = m.toList.sortBy(-_._2.base.nbGames)
|
||||
def merge(o: PerfResultsMap) = PerfResultsMap {
|
||||
m.map {
|
||||
case (k, v) => k -> o.m.get(k).fold(v)(v.merge)
|
||||
}
|
||||
}
|
||||
}
|
||||
val emptyPerfResultsMap = PerfResultsMap(Map.empty)
|
||||
|
||||
case class StatusScores(m: Map[Status, Int]) {
|
||||
def aggregate(s: Status) = copy(m = m + (s -> m.get(s).fold(1)(1+)))
|
||||
def merge(o: StatusScores) = StatusScores {
|
||||
m.map {
|
||||
case (k, v) => k -> (v + ~o.m.get(k))
|
||||
}
|
||||
}
|
||||
def sorted: List[(Status, Int)] = m.toList.sortBy(-_._2)
|
||||
lazy val sum: Int = m.foldLeft(0)(_ + _._2)
|
||||
}
|
||||
|
||||
case class OutcomeStatuses(win: StatusScores, loss: StatusScores) {
|
||||
def aggregate(p: RichPov) = copy(
|
||||
win = if (~p.pov.win) win aggregate p.pov.game.status else win,
|
||||
loss = if (~p.pov.loss) loss aggregate p.pov.game.status else loss)
|
||||
def merge(o: OutcomeStatuses) = OutcomeStatuses(
|
||||
win = win merge o.win,
|
||||
loss = loss merge o.win)
|
||||
}
|
||||
val emptyOutcomeStatuses = OutcomeStatuses(StatusScores(Map.empty), StatusScores(Map.empty))
|
||||
|
||||
val empty = PerfResults(Results.empty, ColorMoves.empty, none, emptyOutcomeStatuses)
|
||||
|
||||
case class Computation(
|
||||
results: PerfResults,
|
||||
base: Results.Computation) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
results = results.aggregate(p),
|
||||
base = base.aggregate(p))
|
||||
|
||||
def nbGames = base.nbGames
|
||||
|
||||
def run = results.copy(base = base.run)
|
||||
}
|
||||
val emptyComputation = Computation(empty, Results.emptyComputation)
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import scalaz.NonEmptyList
|
||||
|
||||
// contains aggregated data over (up to) 100 games
|
||||
case class Period(
|
||||
_id: String, // random
|
||||
userId: String,
|
||||
data: UserStat,
|
||||
from: DateTime,
|
||||
to: DateTime,
|
||||
computedAt: DateTime) {
|
||||
|
||||
def id = _id
|
||||
|
||||
def merge(o: Period) = copy(
|
||||
data = data merge o.data,
|
||||
from = from,
|
||||
to = o.to)
|
||||
|
||||
def isFresh = DateTime.now minusDays 1 isBefore computedAt
|
||||
def isStale = !isFresh
|
||||
|
||||
def nbGames = data.results.base.nbGames
|
||||
}
|
||||
|
||||
object Period {
|
||||
|
||||
val MAX_GAMES = 100
|
||||
|
||||
case class Computation(period: Period, data: UserStat.Computation) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(data = data aggregate p)
|
||||
|
||||
def run = period.copy(data = data.run)
|
||||
|
||||
def nbGames = data.nbGames
|
||||
}
|
||||
def initComputation(userId: String, pov: RichPov) =
|
||||
Computation(build(userId, pov), UserStat.emptyComputation aggregate pov)
|
||||
|
||||
def build(userId: String, pov: RichPov) = Period(
|
||||
_id = ornicar.scalalib.Random nextStringUppercase 8,
|
||||
userId = userId,
|
||||
data = UserStat.empty,
|
||||
from = pov.pov.game.createdAt,
|
||||
to = pov.pov.game.createdAt,
|
||||
computedAt = DateTime.now)
|
||||
}
|
||||
|
||||
case class Periods(periods: NonEmptyList[Period]) {
|
||||
|
||||
lazy val period: Period = periods.tail.foldLeft(periods.head)(_ merge _)
|
||||
}
|
||||
|
||||
object Periods {
|
||||
|
||||
case class Computation(
|
||||
userId: String,
|
||||
save: Period => Funit,
|
||||
period: Option[Period.Computation]) {
|
||||
|
||||
def aggregate(p: RichPov): Fu[Computation] = ((period, p) match {
|
||||
case (None, p) => fuccess {
|
||||
Period.initComputation(userId, p)
|
||||
}
|
||||
case (Some(comp), p) if comp.nbGames >= Period.MAX_GAMES => save(comp.run) inject {
|
||||
Period.initComputation(userId, p)
|
||||
}
|
||||
case (Some(comp), p) => fuccess {
|
||||
comp.aggregate(p)
|
||||
}
|
||||
}) map { comp =>
|
||||
copy(period = comp.some)
|
||||
}
|
||||
|
||||
def run: Funit = period.filter(_.nbGames > 0) ?? { p => save(p.run) }
|
||||
}
|
||||
def initComputation(userId: String, save: Period => Funit) = Computation(userId, save, None)
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import lila.game.Pov
|
||||
|
||||
case class Results(
|
||||
nbGames: Int,
|
||||
nbAnalysis: Int,
|
||||
nbWin: Int,
|
||||
nbLoss: Int,
|
||||
nbDraw: Int,
|
||||
ratingDiff: Int,
|
||||
gameSections: GameSections,
|
||||
bestWin: Option[Results.BestWin],
|
||||
opponentRatingSum: Int,
|
||||
lastPlayed: Option[DateTime]) {
|
||||
|
||||
def opponentRatingAvg = (nbGames > 0) option (opponentRatingSum / nbGames)
|
||||
|
||||
def ratingDiffAvg = (nbGames > 0) option (ratingDiff / nbGames)
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
nbGames = nbGames + 1,
|
||||
nbAnalysis = nbAnalysis + p.analysis.isDefined.fold(1, 0),
|
||||
nbWin = nbWin + (~p.pov.win).fold(1, 0),
|
||||
nbLoss = nbLoss + (~p.pov.loss).fold(1, 0),
|
||||
nbDraw = nbDraw + p.pov.game.drawn.fold(1, 0),
|
||||
ratingDiff = ratingDiff + ~p.pov.player.ratingDiff,
|
||||
gameSections = gameSections aggregate p,
|
||||
bestWin = if (~p.pov.win) {
|
||||
Results.makeBestWin(p.pov).fold(bestWin) { newBest =>
|
||||
bestWin.fold(newBest) { prev =>
|
||||
if (newBest.rating > prev.rating) newBest else prev
|
||||
}.some
|
||||
}
|
||||
}
|
||||
else bestWin,
|
||||
opponentRatingSum = opponentRatingSum + ~p.pov.opponent.rating,
|
||||
lastPlayed = p.pov.game.createdAt.some)
|
||||
|
||||
def merge(r: Results) = Results(
|
||||
nbGames = nbGames + r.nbGames,
|
||||
nbAnalysis = nbAnalysis + r.nbAnalysis,
|
||||
nbWin = nbWin + r.nbWin,
|
||||
nbLoss = nbLoss + r.nbLoss,
|
||||
nbDraw = nbDraw + r.nbDraw,
|
||||
ratingDiff = ratingDiff + r.ratingDiff,
|
||||
gameSections = gameSections merge r.gameSections,
|
||||
bestWin = (bestWin, r.bestWin) match {
|
||||
case (Some(a), Some(b)) => Some(a merge b)
|
||||
case (Some(a), _) => a.some
|
||||
case (_, Some(b)) => b.some
|
||||
case _ => none
|
||||
},
|
||||
opponentRatingSum = opponentRatingSum + r.opponentRatingSum,
|
||||
lastPlayed = (lastPlayed, r.lastPlayed) match {
|
||||
case (Some(a), Some(b)) => Some(if (a isAfter b) a else b)
|
||||
case (Some(a), _) => a.some
|
||||
case (_, Some(b)) => b.some
|
||||
case _ => none
|
||||
})
|
||||
}
|
||||
|
||||
object Results {
|
||||
|
||||
val empty = Results(0, 0, 0, 0, 0, 0, GameSections.empty, none, 0, none)
|
||||
|
||||
case class BestWin(id: String, userId: String, rating: Int) {
|
||||
def merge(b: BestWin) = if (rating > b.rating) this else b
|
||||
}
|
||||
def makeBestWin(pov: Pov): Option[BestWin] = (pov.game.playedTurns > 4) ?? {
|
||||
pov.opponent.userId |@| pov.opponent.rating apply {
|
||||
case (opId, opRating) => BestWin(pov.gameId, opId, opRating)
|
||||
}
|
||||
}
|
||||
|
||||
case class Computation(results: Results) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(results = results.aggregate(p))
|
||||
|
||||
def nbGames = results.nbGames
|
||||
|
||||
def run = results
|
||||
}
|
||||
val emptyComputation = Computation(empty)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import lila.pref.Pref
|
||||
import lila.user.User
|
||||
|
||||
final class Share(
|
||||
getPref: String => Fu[Pref],
|
||||
areFriends: (String, String) => Fu[Boolean]) {
|
||||
|
||||
def grant(coached: User, to: Option[User]): Fu[Boolean] = getPref(coached.id) flatMap { pref =>
|
||||
pref.coachShare match {
|
||||
case _ if to.contains(coached) => fuccess(true)
|
||||
case Pref.CoachShare.EVERYBODY => fuccess(true)
|
||||
case Pref.CoachShare.FRIENDS => to ?? { t => areFriends(coached.id, t.id) }
|
||||
case Pref.CoachShare.NOBODY => fuccess(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
||||
import reactivemongo.bson._
|
||||
import reactivemongo.bson.Macros
|
||||
import reactivemongo.core.commands._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.db.BSON._
|
||||
import lila.db.Implicits._
|
||||
import lila.user.UserRepo
|
||||
|
||||
final class StatApi(coll: Coll) {
|
||||
|
||||
import BSONHandlers._
|
||||
|
||||
private def selectId(id: String) = BSONDocument("_id" -> id)
|
||||
private def selectUserId(id: String) = BSONDocument("userId" -> id)
|
||||
private val sortChronological = BSONDocument("from" -> 1)
|
||||
private val sortAntiChronological = BSONDocument("from" -> -1)
|
||||
|
||||
def fetchRangeForMoves(userId: String, range: Range) =
|
||||
fetchRange(userId, range, BSONDocument("data.openings" -> false))
|
||||
|
||||
def fetchRangeForOpenings(userId: String, range: Range) =
|
||||
fetchRange(userId, range, BSONDocument("data.perfResults" -> false))
|
||||
|
||||
private def fetchRange(
|
||||
userId: String,
|
||||
range: Range,
|
||||
projection: BSONDocument): Fu[Option[Period]] =
|
||||
coll.find(selectUserId(userId), projection)
|
||||
.skip(range.min)
|
||||
.sort(sortChronological)
|
||||
.cursor[Period]()
|
||||
.enumerate(range.size) &>
|
||||
Enumeratee.take(range.size) |>>>
|
||||
Iteratee.fold[Period, Option[Period]](none) {
|
||||
case (a, b) => a.fold(b)(_ merge b).some
|
||||
}
|
||||
|
||||
def fetchFirst(userId: String): Fu[Option[Period]] =
|
||||
fetchRange(userId, Range(0, 1), BSONDocument())
|
||||
|
||||
def fetchLast(userId: String): Fu[Option[Period]] =
|
||||
coll.find(selectUserId(userId)).sort(sortAntiChronological).one[Period]
|
||||
|
||||
def count(userId: String): Fu[Int] =
|
||||
coll.count(selectUserId(userId).some)
|
||||
|
||||
def insert(p: Period) = coll.insert(p).void
|
||||
|
||||
def remove(p: Period) = coll.remove(selectId(p.id)).void
|
||||
|
||||
def removeAll(userId: String) = coll.remove(selectUserId(userId)).void
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
import org.joda.time.DateTime
|
||||
|
||||
import lila.rating.PerfType
|
||||
|
||||
case class UserStat(
|
||||
colorResults: ColorResults,
|
||||
openings: Openings,
|
||||
results: PerfResults,
|
||||
perfResults: PerfResults.PerfResultsMap) {
|
||||
|
||||
def merge(o: UserStat) = copy(
|
||||
colorResults = colorResults merge o.colorResults,
|
||||
openings = openings merge o.openings,
|
||||
results = results merge o.results,
|
||||
perfResults = perfResults merge o.perfResults)
|
||||
}
|
||||
|
||||
object UserStat {
|
||||
|
||||
case class Computation(
|
||||
stat: UserStat,
|
||||
colorResultsComp: ColorResults.Computation,
|
||||
resultsComp: PerfResults.Computation,
|
||||
perfResultsComp: Map[PerfType, PerfResults.Computation],
|
||||
openingsComp: Openings.Computation) {
|
||||
|
||||
def aggregate(p: RichPov) = copy(
|
||||
resultsComp = resultsComp aggregate p,
|
||||
colorResultsComp = colorResultsComp aggregate p,
|
||||
perfResultsComp = p.pov.game.perfType.fold(perfResultsComp) { perfType =>
|
||||
perfResultsComp + (
|
||||
perfType ->
|
||||
(perfResultsComp.get(perfType) | PerfResults.emptyComputation).aggregate(p)
|
||||
)
|
||||
},
|
||||
openingsComp = openingsComp.aggregate(p))
|
||||
|
||||
def nbGames = resultsComp.nbGames
|
||||
|
||||
def run = stat.copy(
|
||||
results = resultsComp.run,
|
||||
colorResults = colorResultsComp.run,
|
||||
perfResults = PerfResults.PerfResultsMap(perfResultsComp.mapValues(_.run)),
|
||||
openings = openingsComp.run)
|
||||
}
|
||||
|
||||
val empty = UserStat(
|
||||
colorResults = ColorResults.empty,
|
||||
openings = Openings.empty,
|
||||
results = PerfResults.empty,
|
||||
perfResults = PerfResults.emptyPerfResultsMap)
|
||||
|
||||
val emptyComputation = Computation(empty, ColorResults.emptyComputation, PerfResults.emptyComputation, Map.empty, Openings.emptyComputation)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package lila.coach
|
||||
|
||||
case class RichPov(
|
||||
pov: lila.game.Pov,
|
||||
initialFen: Option[String],
|
||||
analysis: Option[lila.analyse.Analysis],
|
||||
division: chess.Division,
|
||||
accuracy: Option[lila.analyse.Accuracy.DividedAccuracy],
|
||||
moveAccuracy: Option[List[Int]])
|
||||
|
||||
case class NbSum(nb: Int, sum: Int) {
|
||||
|
||||
def avg = (nb > 0) option (sum / nb)
|
||||
|
||||
def add(v: Int) = copy(nb + 1, sum + v)
|
||||
|
||||
def merge(o: NbSum) = NbSum(nb + o.nb, sum + o.sum)
|
||||
}
|
||||
object NbSum {
|
||||
val empty = NbSum(0, 0)
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package lila
|
||||
|
||||
package object coach extends PackageObject with WithPlay
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
|
||||
import reactivemongo.bson._
|
||||
|
@ -8,7 +8,7 @@ import lila.db.Implicits._
|
|||
|
||||
private final class AggregationPipeline {
|
||||
|
||||
import lila.coach.{ Dimension => D, Metric => M }
|
||||
import lila.insight.{ Dimension => D, Metric => M }
|
||||
import Storage._
|
||||
|
||||
private val unwindMoves = Unwind("moves").some
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
case class Answer[X](
|
||||
question: Question[X],
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import reactivemongo.bson._
|
||||
import reactivemongo.bson.Macros
|
||||
|
@ -10,7 +10,7 @@ import lila.db.Implicits._
|
|||
import lila.game.BSONHandlers.StatusBSONHandler
|
||||
import lila.rating.PerfType
|
||||
|
||||
private[coach] object BSONHandlers {
|
||||
private object BSONHandlers {
|
||||
|
||||
implicit val ColorBSONHandler = new BSONHandler[BSONBoolean, Color] {
|
||||
def read(b: BSONBoolean) = Color(b.value)
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import play.api.libs.json._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import reactivemongo.bson._
|
||||
|
||||
|
@ -53,8 +53,8 @@ object Dimension {
|
|||
|
||||
def valuesOf[X](d: Dimension[X]): List[X] = d match {
|
||||
case Perf => PerfType.nonPuzzle
|
||||
case Phase => lila.coach.Phase.all
|
||||
case Result => lila.coach.Result.all
|
||||
case Phase => lila.insight.Phase.all
|
||||
case Result => lila.insight.Result.all
|
||||
case Color => chess.Color.all
|
||||
case Opening => EcopeningDB.all
|
||||
case OpponentStrength => RelativeStrength.all
|
||||
|
@ -63,8 +63,8 @@ object Dimension {
|
|||
|
||||
def valueByKey[X](d: Dimension[X], key: String): Option[X] = d match {
|
||||
case Perf => PerfType.byKey get key
|
||||
case Phase => parseIntOption(key) flatMap lila.coach.Phase.byId.get
|
||||
case Result => parseIntOption(key) flatMap lila.coach.Result.byId.get
|
||||
case Phase => parseIntOption(key) flatMap lila.insight.Phase.byId.get
|
||||
case Result => parseIntOption(key) flatMap lila.insight.Result.byId.get
|
||||
case Color => chess.Color(key)
|
||||
case Opening => EcopeningDB.allByEco get key
|
||||
case OpponentStrength => parseIntOption(key) flatMap RelativeStrength.byId.get
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
case class Ecopening(
|
||||
eco: Ecopening.ECO,
|
|
@ -1,6 +1,6 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
private[coach] object EcopeningDB {
|
||||
private object EcopeningDB {
|
||||
|
||||
import Ecopening._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import chess.{ Color, Status, Role }
|
||||
import lila.game.{ PgnMoves, Game }
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import akka.actor._
|
||||
import com.typesafe.config.Config
|
||||
|
@ -30,7 +30,7 @@ final class Env(
|
|||
|
||||
private lazy val aggregationPipeline = new AggregationPipeline
|
||||
|
||||
lazy val api = new CoachApi(
|
||||
lazy val api = new InsightApi(
|
||||
storage = storage,
|
||||
pipeline = aggregationPipeline)
|
||||
|
||||
|
@ -41,8 +41,8 @@ final class Env(
|
|||
|
||||
object Env {
|
||||
|
||||
lazy val current: Env = "coach" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "coach",
|
||||
lazy val current: Env = "insight" boot new Env(
|
||||
config = lila.common.PlayApp loadConfig "insight",
|
||||
getPref = lila.pref.Env.current.api.getPrefById,
|
||||
areFriends = lila.relation.Env.current.api.areFriends,
|
||||
lightUser = lila.user.Env.current.lightUser,
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import org.joda.time.DateTime
|
||||
|
@ -48,8 +48,8 @@ final class Indexer(storage: Storage, sequencer: ActorRef) {
|
|||
pimpQB($query(gameQuery(user))).sort(Query.sortChronological).one[Game]
|
||||
|
||||
private def computeFrom(user: User, from: DateTime): Funit =
|
||||
lila.common.Chronometer.log(s"coach aggregator:${user.username}") {
|
||||
loginfo(s"[coach] start aggregating ${user.username} games")
|
||||
lila.common.Chronometer.log(s"insight aggregator:${user.username}") {
|
||||
loginfo(s"[insight] start aggregating ${user.username} games")
|
||||
val query = $query(gameQuery(user) ++ Json.obj(Game.BSONFields.createdAt -> $gte($date(from))))
|
||||
// val query = $query(gameQuery(user) ++ Query.analysed(true) ++ Json.obj(Game.BSONFields.createdAt -> $gte($date(from))))
|
||||
pimpQB(query)
|
||||
|
@ -64,13 +64,13 @@ final class Indexer(storage: Storage, sequencer: ActorRef) {
|
|||
} |>>>
|
||||
Iteratee.foldM[Either[Game, Entry], Int](0) {
|
||||
case (nb, Right(e)) =>
|
||||
if (nb % 100 == 0) loginfo(s"[coach ${user.username}] aggregated $nb games")
|
||||
if (nb % 100 == 0) loginfo(s"[insight ${user.username}] aggregated $nb games")
|
||||
storage insert e inject (nb + 1)
|
||||
case (nb, Left(g)) =>
|
||||
logwarn(s"[coach ${user.username}] invalid game http://l.org/${g.id}")
|
||||
logwarn(s"[insight ${user.username}] invalid game http://l.org/${g.id}")
|
||||
fuccess(nb)
|
||||
} addEffect { nb =>
|
||||
loginfo(s"[coach ${user.username}] done aggregating $nb games")
|
||||
loginfo(s"[insight ${user.username}] done aggregating $nb games")
|
||||
} void
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework._
|
||||
import reactivemongo.bson._
|
||||
|
@ -7,12 +7,12 @@ import lila.db.Implicits._
|
|||
import lila.game.GameRepo
|
||||
import lila.user.User
|
||||
|
||||
final class CoachApi(
|
||||
final class InsightApi(
|
||||
storage: Storage,
|
||||
pipeline: AggregationPipeline) {
|
||||
|
||||
import lila.coach.{ Dimension => D, Metric => M }
|
||||
import CoachApi._
|
||||
import lila.insight.{ Dimension => D, Metric => M }
|
||||
import InsightApi._
|
||||
|
||||
def ask[X](question: Question[X], user: User): Fu[Answer[X]] =
|
||||
storage.aggregate(pipeline(question, user.id)).map { res =>
|
||||
|
@ -48,7 +48,7 @@ final class CoachApi(
|
|||
}
|
||||
}
|
||||
|
||||
object CoachApi {
|
||||
object InsightApi {
|
||||
|
||||
sealed trait UserStatus
|
||||
object UserStatus {
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import play.api.libs.json._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import play.api.libs.json._
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import scala.annotation._
|
||||
import scala.math.{ pow, abs, sqrt, E, exp }
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
sealed abstract class Metric(
|
||||
val key: String,
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
sealed trait Position {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import chess.Role
|
||||
import lila.analyse.Accuracy
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
case class Question[X](
|
||||
dimension: Dimension[X],
|
|
@ -0,0 +1,18 @@
|
|||
package lila.insight
|
||||
|
||||
import lila.pref.Pref
|
||||
import lila.user.User
|
||||
|
||||
final class Share(
|
||||
getPref: String => Fu[Pref],
|
||||
areFriends: (String, String) => Fu[Boolean]) {
|
||||
|
||||
def grant(insighted: User, to: Option[User]): Fu[Boolean] = getPref(insighted.id) flatMap { pref =>
|
||||
pref.insightShare match {
|
||||
case _ if to.contains(insighted) => fuccess(true)
|
||||
case Pref.InsightShare.EVERYBODY => fuccess(true)
|
||||
case Pref.InsightShare.FRIENDS => to ?? { t => areFriends(insighted.id, t.id) }
|
||||
case Pref.InsightShare.NOBODY => fuccess(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package lila.coach
|
||||
package lila.insight
|
||||
|
||||
import org.joda.time.DateTime
|
||||
import play.api.libs.iteratee._
|
|
@ -0,0 +1,3 @@
|
|||
package lila
|
||||
|
||||
package object insight extends PackageObject with WithPlay
|
|
@ -25,7 +25,7 @@ private[pref] final class DataForm {
|
|||
"premove" -> number.verifying(Set(0, 1) contains _),
|
||||
"animation" -> number.verifying(Set(0, 1, 2, 3) contains _),
|
||||
"submitMove" -> number.verifying(Pref.SubmitMove.choices.toMap contains _),
|
||||
"coachShare" -> number.verifying(Set(0, 1, 2) contains _),
|
||||
"insightShare" -> number.verifying(Set(0, 1, 2) contains _),
|
||||
"confirmResign" -> number.verifying(Pref.ConfirmResign.choices.toMap contains _),
|
||||
"captured" -> number.verifying(Set(0, 1) contains _)
|
||||
)(PrefData.apply)(PrefData.unapply))
|
||||
|
@ -48,7 +48,7 @@ private[pref] final class DataForm {
|
|||
premove: Int,
|
||||
animation: Int,
|
||||
submitMove: Int,
|
||||
coachShare: Int,
|
||||
insightShare: Int,
|
||||
confirmResign: Int,
|
||||
captured: Int) {
|
||||
|
||||
|
@ -70,7 +70,7 @@ private[pref] final class DataForm {
|
|||
premove = premove == 1,
|
||||
animation = animation,
|
||||
submitMove = submitMove,
|
||||
coachShare = coachShare,
|
||||
insightShare = insightShare,
|
||||
confirmResign = confirmResign,
|
||||
captured = captured == 1)
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ private[pref] final class DataForm {
|
|||
premove = pref.premove.fold(1, 0),
|
||||
animation = pref.animation,
|
||||
submitMove = pref.submitMove,
|
||||
coachShare = pref.coachShare,
|
||||
insightShare = pref.insightShare,
|
||||
confirmResign = pref.confirmResign,
|
||||
captured = pref.captured.fold(1, 0))
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ case class Pref(
|
|||
puzzleDifficulty: Int,
|
||||
submitMove: Int,
|
||||
confirmResign: Int,
|
||||
coachShare: Int,
|
||||
insightShare: Int,
|
||||
tags: Map[String, String] = Map.empty) {
|
||||
|
||||
import Pref._
|
||||
|
@ -157,7 +157,7 @@ object Pref {
|
|||
YES -> "Yes")
|
||||
}
|
||||
|
||||
object CoachShare {
|
||||
object InsightShare {
|
||||
val NOBODY = 0
|
||||
val FRIENDS = 1
|
||||
val EVERYBODY = 2
|
||||
|
@ -314,7 +314,7 @@ object Pref {
|
|||
puzzleDifficulty = Difficulty.NORMAL,
|
||||
submitMove = SubmitMove.CORRESPONDENCE_ONLY,
|
||||
confirmResign = ConfirmResign.YES,
|
||||
coachShare = CoachShare.FRIENDS,
|
||||
insightShare = InsightShare.FRIENDS,
|
||||
tags = Map.empty)
|
||||
|
||||
import ornicar.scalalib.Zero
|
||||
|
|
|
@ -56,7 +56,7 @@ final class PrefApi(
|
|||
puzzleDifficulty = r.getD("puzzleDifficulty", Pref.default.puzzleDifficulty),
|
||||
submitMove = r.getD("submitMove", Pref.default.submitMove),
|
||||
confirmResign = r.getD("confirmResign", Pref.default.confirmResign),
|
||||
coachShare = r.getD("coachShare", Pref.default.coachShare),
|
||||
insightShare = r.getD("insightShare", Pref.default.insightShare),
|
||||
tags = r.getD("tags", Pref.default.tags))
|
||||
|
||||
def writes(w: BSON.Writer, o: Pref) = BSONDocument(
|
||||
|
@ -91,7 +91,7 @@ final class PrefApi(
|
|||
"puzzleDifficulty" -> o.puzzleDifficulty,
|
||||
"submitMove" -> o.submitMove,
|
||||
"confirmResign" -> o.confirmResign,
|
||||
"coachShare" -> o.coachShare,
|
||||
"insightShare" -> o.insightShare,
|
||||
"tags" -> o.tags)
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ object ApplicationBuild extends Build {
|
|||
importer, tournament, simul, relation, report, pref, // simulation,
|
||||
evaluation, chat, puzzle, tv, coordinate, blog, donation, qa,
|
||||
history, worldMap, opening, video, shutup,
|
||||
playban, coach)
|
||||
playban, insight)
|
||||
|
||||
lazy val moduleRefs = modules map projectToRef
|
||||
lazy val moduleCPDeps = moduleRefs map { new sbt.ClasspathDependency(_, None) }
|
||||
|
@ -189,7 +189,7 @@ object ApplicationBuild extends Build {
|
|||
libraryDependencies ++= provided(play.api, RM, PRM)
|
||||
)
|
||||
|
||||
lazy val coach = project("coach",
|
||||
lazy val insight = project("insight",
|
||||
Seq(common, chess, game, user, analyse, relation, pref, socket, round)
|
||||
).settings(
|
||||
libraryDependencies ++= provided(play.api, RM, PRM)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
$(function() {
|
||||
lichess.refreshCoachForm = function() {
|
||||
$('form.coach-refresh').submit(function() {
|
||||
$('form.insight-refresh').submit(function() {
|
||||
$.modal($(this).find('.crunshing'));
|
||||
$.post($(this).attr('action'), function() {
|
||||
location.reload();
|
|
@ -1,5 +0,0 @@
|
|||
.coach_main .inspect .movechart {
|
||||
height: 340px;
|
||||
width: 772px;
|
||||
margin-left: -10px;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
.coach_main .inspect .board {
|
||||
float: left;
|
||||
}
|
||||
.coach_main .inspect .content {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.coach_main .inspect .cg-board-wrap {
|
||||
width: 330px;
|
||||
height: 330px;
|
||||
position: relative;
|
||||
}
|
||||
.coach_main .inspect .right {
|
||||
position: relative;
|
||||
margin-left: 350px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.coach_main .inspect .right .not_analysed {
|
||||
line-height: 120px;
|
||||
}
|
||||
.coach_main .inspect .right h3 {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
font-size: 0.9em;
|
||||
margin: 0;
|
||||
}
|
||||
.coach_main .inspect .right .sections {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
}
|
||||
.coach_main .inspect .right table {
|
||||
width: 100%;
|
||||
line-height: 2em;
|
||||
}
|
|
@ -1,54 +1,54 @@
|
|||
#coach .square-wrap {
|
||||
#insight .square-wrap {
|
||||
text-align: center;
|
||||
margin-top: 180px;
|
||||
}
|
||||
#coach .square-in {
|
||||
#insight .square-in {
|
||||
-webkit-transform: scale(5);
|
||||
}
|
||||
|
||||
#coach .left {
|
||||
#insight .left {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: -260px;
|
||||
width: 240px;
|
||||
}
|
||||
#coach .coach-stale {
|
||||
#insight .insight-stale {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#coach .ms-parent {
|
||||
#insight .ms-parent {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#coach .ms-parent > * {
|
||||
#insight .ms-parent > * {
|
||||
position: absoule;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
#coach .ms-choice {
|
||||
#insight .ms-choice {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#coach .ms-drop {
|
||||
#insight .ms-drop {
|
||||
margin-left: 100%;
|
||||
}
|
||||
#coach .ms-drop ul {
|
||||
#insight .ms-drop ul {
|
||||
padding: 0;
|
||||
}
|
||||
#coach .ms-drop ul > li label {
|
||||
#insight .ms-drop ul > li label {
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#coach .ms-drop ul > li label:hover {
|
||||
#insight .ms-drop ul > li label:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
#coach .ms-drop ul > li label input {
|
||||
#insight .ms-drop ul > li label input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#coach table {
|
||||
#insight table {
|
||||
border: 1px solid #d4d4d4;
|
||||
}
|
||||
#coach table td.data {
|
||||
#insight table td.data {
|
||||
font-weight: bold;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
var source = require('vinyl-source-stream');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var watchify = require('watchify');
|
||||
var browserify = require('browserify');
|
||||
var uglify = require('gulp-uglify');
|
||||
var streamify = require('gulp-streamify');
|
||||
|
||||
var sources = ['./src/main.js'];
|
||||
var destination = '../../public/compiled/';
|
||||
var onError = function(error) {
|
||||
gutil.log(gutil.colors.red(error.message));
|
||||
};
|
||||
var standalone = 'LichessCoachMove';
|
||||
|
||||
gulp.task('prod', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.move.min.js'))
|
||||
.pipe(streamify(uglify()))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('dev', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.move.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
var opts = watchify.args;
|
||||
opts.debug = true;
|
||||
opts.standalone = standalone;
|
||||
|
||||
var bundleStream = watchify(browserify(sources, opts))
|
||||
.on('update', rebundle)
|
||||
.on('log', gutil.log);
|
||||
|
||||
function rebundle() {
|
||||
return bundleStream.bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.move.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
}
|
||||
|
||||
return rebundle();
|
||||
});
|
||||
|
||||
gulp.task('default', ['watch']);
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"name": "coachMove",
|
||||
"version": "1.0.0",
|
||||
"description": "lichess.org move coach",
|
||||
"main": "src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ornicar/lila"
|
||||
},
|
||||
"keywords": [
|
||||
"chess",
|
||||
"lichess",
|
||||
"coach",
|
||||
"move"
|
||||
],
|
||||
"author": "ornicar",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ornicar/lila/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ornicar/lila",
|
||||
"devDependencies": {
|
||||
"browserify": "~9.0.8",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-streamify": "~0.0.5",
|
||||
"gulp-uglify": "~1.2.0",
|
||||
"gulp-util": "~3.0.4",
|
||||
"vinyl-source-stream": "~1.1.0",
|
||||
"watchify": "~3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chessground": "github:ornicar/chessground#v3.0.4",
|
||||
"coach": "../coach",
|
||||
"mithril": "github:ornicar/mithril.js#v1.0.0"
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var throttle = require('coach').throttle;
|
||||
|
||||
module.exports = function(opts) {
|
||||
|
||||
this.user = opts.user;
|
||||
this.color = opts.color;
|
||||
this.nbPeriods = opts.nbPeriods;
|
||||
|
||||
this.vm = {
|
||||
preloading: !!this.nbPeriods,
|
||||
loading: true,
|
||||
range: [0, this.nbPeriods],
|
||||
inspecting: 'global'
|
||||
};
|
||||
|
||||
var requestData = throttle(1000, false, function() {
|
||||
m.request({
|
||||
url: '/coach/move/' + this.user.id + '.json',
|
||||
data: {
|
||||
range: this.vm.range.join('-')
|
||||
}
|
||||
}).then(function(data) {
|
||||
console.log(data);
|
||||
this.data = data;
|
||||
if (location.hash) this.inspect(location.hash.replace(/#/, '').replace(/_/g, ' '));
|
||||
this.vm.preloading = false;
|
||||
this.vm.loading = false;
|
||||
}.bind(this));
|
||||
m.redraw();
|
||||
}.bind(this));
|
||||
if (this.nbPeriods) setTimeout(requestData, 200);
|
||||
|
||||
this.selectPeriodRange = function(from, to) {
|
||||
this.vm.range = [from, to];
|
||||
this.vm.loading = true;
|
||||
if (from === to) this.data = null;
|
||||
else requestData();
|
||||
m.redraw();
|
||||
}.bind(this);
|
||||
|
||||
this.jumpBy = function(delta) {
|
||||
if (!this.vm.inspecting) return;
|
||||
var keys = this.data.perfs.map(function(o) {
|
||||
return o.perf.key
|
||||
});
|
||||
var i = keys.indexOf(this.vm.inspecting);
|
||||
var i2 = (i + delta) % keys.length;
|
||||
if (i2 < 0) i2 = keys.length - 1;
|
||||
this.inspect(keys[i2]);
|
||||
}.bind(this);
|
||||
|
||||
this.inspect = function(key) {
|
||||
if (!this.data || !this.data.perfs.some(function(o) {
|
||||
return o.perf.key === key;
|
||||
})) return;
|
||||
if (this.vm.inspecting === key) return;
|
||||
if (window.history.replaceState)
|
||||
window.history.replaceState(null, null, '#' + key);
|
||||
this.vm.inspecting = key;
|
||||
}.bind(this);
|
||||
|
||||
this.trans = lichess.trans(opts.i18n);
|
||||
};
|
|
@ -1,57 +0,0 @@
|
|||
var m = require('mithril');
|
||||
var chessground = require('chessground');
|
||||
|
||||
var coach = require('coach');
|
||||
var movechart = require('./movechart');
|
||||
|
||||
function sideCommands(ctrl) {
|
||||
return [
|
||||
m('a.to.prev', {
|
||||
'data-icon': 'I',
|
||||
onclick: function() {
|
||||
ctrl.jumpBy(-1);
|
||||
}
|
||||
}),
|
||||
m('a.to.next', {
|
||||
'data-icon': 'H',
|
||||
onclick: function() {
|
||||
ctrl.jumpBy(1);
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
var d = ctrl.data;
|
||||
var o = d.perfs.filter(function(o) {
|
||||
return o.perf.key === ctrl.vm.inspecting;
|
||||
})[0];
|
||||
if (!o) return m('div.top.nodata', [
|
||||
sideCommands(ctrl),
|
||||
m('p', 'No results for this data range and perf!')
|
||||
]);
|
||||
var perf = o.perf;
|
||||
var perfResults = o.results;
|
||||
var results = perfResults.base;
|
||||
return m('div.top.inspect', [
|
||||
sideCommands(ctrl),
|
||||
coach.resultBar(results),
|
||||
m('div.main.clearfix', [
|
||||
coach.shared.progress(results.ratingDiff / results.nbGames),
|
||||
m('h2', m('strong.text', {
|
||||
'data-icon': perf.icon
|
||||
}, perf.name)),
|
||||
m('div.baseline', [
|
||||
m('strong', results.nbGames),
|
||||
' games, ',
|
||||
m('strong', results.nbAnalysis),
|
||||
' analysed. Last played ',
|
||||
coach.shared.momentFromNow(results.lastPlayed),
|
||||
'.',
|
||||
])
|
||||
]),
|
||||
m('div.content', [
|
||||
movechart(ctrl),
|
||||
])
|
||||
]);
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var ctrl = require('./ctrl');
|
||||
var view = require('./view');
|
||||
|
||||
module.exports = function(element, opts) {
|
||||
|
||||
var controller = new ctrl(opts);
|
||||
|
||||
m.module(element, {
|
||||
controller: function() {
|
||||
return controller;
|
||||
},
|
||||
view: view
|
||||
});
|
||||
};
|
|
@ -1,182 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var green = '#759900',
|
||||
red = '#dc322f',
|
||||
orange = '#d59120',
|
||||
translucid = 'rgba(0,0,0,0.3)';
|
||||
|
||||
var absFormatter = function() {
|
||||
return Math.abs(this.value);
|
||||
};
|
||||
|
||||
function makeChart(el, data) {
|
||||
$(el).highcharts({
|
||||
chart: {
|
||||
spacing: [0, 0, 0, 0],
|
||||
animation: {
|
||||
duration: 300
|
||||
},
|
||||
backgroundColor: null,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
plotBackgroundColor: null,
|
||||
plotShadow: false,
|
||||
plotBorderWidth: 0
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
xAxis: {},
|
||||
yAxis: [{
|
||||
min: -150,
|
||||
max: 150,
|
||||
title: {
|
||||
text: 'Average centipawn loss (ACPL) per move'
|
||||
},
|
||||
labels: {
|
||||
formatter: absFormatter
|
||||
},
|
||||
lineWidth: 1,
|
||||
gridLineWidth: 1
|
||||
}, {
|
||||
opposite: true,
|
||||
title: {
|
||||
text: 'Seconds per move'
|
||||
},
|
||||
labels: {
|
||||
formatter: absFormatter
|
||||
},
|
||||
lineWidth: 1,
|
||||
gridLineWidth: 1
|
||||
}],
|
||||
plotOptions: {
|
||||
column: {
|
||||
stacking: 'normal'
|
||||
},
|
||||
line: {
|
||||
color: 'rgba(0,0,0,0.7)',
|
||||
lineWidth: 1
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
useHTML: true,
|
||||
formatter: function() {
|
||||
var base = '[move ' + this.point.x + ' as ' + this.point.c + '] <b>';
|
||||
if (this.point.acpl)
|
||||
return base + Math.abs(this.point.acpl.avg) + '</b> centipawns<hr>' +
|
||||
'over ' + this.point.acpl.nb + ' analysed games';
|
||||
return base + Math.abs(this.point.time.avg / 10) + '</b> seconds<hr>' +
|
||||
'over ' + this.point.time.nb + ' games';
|
||||
},
|
||||
},
|
||||
series: [{
|
||||
yAxis: 0,
|
||||
type: 'column',
|
||||
name: 'ACPL as white',
|
||||
data: data.acpls.white,
|
||||
pointWidth: 9
|
||||
}, {
|
||||
yAxis: 0,
|
||||
type: 'column',
|
||||
name: 'ACPL as black',
|
||||
data: data.acpls.black,
|
||||
// pointPadding: -0.2,
|
||||
pointWidth: 9
|
||||
}, {
|
||||
yAxis: 1,
|
||||
type: 'line',
|
||||
name: 'Time as white',
|
||||
data: data.times.white,
|
||||
marker: {
|
||||
radius: 3
|
||||
}
|
||||
}, {
|
||||
yAxis: 1,
|
||||
type: 'line',
|
||||
name: 'Time as black',
|
||||
data: data.times.black,
|
||||
}],
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
}
|
||||
}, recenter);
|
||||
return $(el).highcharts();
|
||||
}
|
||||
|
||||
function recenter(chart) {
|
||||
[0, 1].forEach(function(i) {
|
||||
var ext = chart.yAxis[i].getExtremes();
|
||||
var dMax = Math.abs(ext.dataMax);
|
||||
var dMin = Math.abs(ext.dataMin);
|
||||
var dExt = dMax >= dMin ? dMax : dMin;
|
||||
var min = 0 - dExt;
|
||||
console.log(i, min, dExt);
|
||||
chart.yAxis[i].setExtremes(min, dExt);
|
||||
});
|
||||
}
|
||||
|
||||
function makeAcplData(pr) {
|
||||
var data = {};
|
||||
['white', 'black'].forEach(function(color) {
|
||||
data[color] = pr.moves[color].filter(function(m) {
|
||||
return m.acpl.nb > 0;
|
||||
}).map(function(move, i) {
|
||||
var acpl = move.acpl.avg;
|
||||
var y = Math.min(acpl, 150) + 10;
|
||||
return {
|
||||
c: color,
|
||||
x: i + 1,
|
||||
y: color === 'white' ? y : -y,
|
||||
color: (acpl < 50 ? green : (acpl < 100 ? orange : red)),
|
||||
acpl: move.acpl
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function makeTimeData(pr) {
|
||||
var data = {};
|
||||
['white', 'black'].forEach(function(color) {
|
||||
data[color] = pr.moves[color].filter(function(m) {
|
||||
return m.time.nb > 0;
|
||||
}).map(function(move, i) {
|
||||
var time = move.time.avg / 10;
|
||||
return {
|
||||
c: color,
|
||||
x: i + 1,
|
||||
y: color === 'white' ? time : -time,
|
||||
time: move.time
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
var pr = ctrl.data.perfs.filter(function(p) {
|
||||
return p.perf.key === ctrl.vm.inspecting;
|
||||
})[0].results;
|
||||
return [
|
||||
m('div.movechart', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
var data = {
|
||||
acpls: makeAcplData(pr),
|
||||
times: makeTimeData(pr)
|
||||
};
|
||||
console.log(data);
|
||||
if (ctx.chart) {
|
||||
[data.acpls.white, data.acpls.black, data.times.white, data.times.black].forEach(function(d, i) {
|
||||
ctx.chart.series[i].setData(d);
|
||||
});
|
||||
recenter(ctx.chart);
|
||||
} else ctx.chart = makeChart(el, data);
|
||||
}
|
||||
})
|
||||
];
|
||||
};
|
|
@ -1,72 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var coach = require('coach');
|
||||
var strings = coach.shared.strings;
|
||||
|
||||
var headers = [
|
||||
['name', 'Category'],
|
||||
['nbGames', 'Games'],
|
||||
['result', 'Result', strings.result],
|
||||
['ratingDiffAvg', 'Rating', strings.ratingDiff],
|
||||
['acpl', 'ACPL', strings.acpl],
|
||||
['lastPlayed', 'Last played']
|
||||
];
|
||||
|
||||
function thead(ctrl) {
|
||||
return m('thead', m('tr', headers.map(function(h) {
|
||||
var props = {
|
||||
key: h[0],
|
||||
};
|
||||
var spanProps = {};
|
||||
if (h[2]) {
|
||||
spanProps.class = 'hint--top';
|
||||
spanProps['data-hint'] = h[2];
|
||||
}
|
||||
return m('th', props, m('span', spanProps, h[1]));
|
||||
})));
|
||||
}
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
var d = ctrl.data;
|
||||
var percent = function(nb) {
|
||||
return Math.round(nb * 100 / d.perfs[0].results.base.nbGames);
|
||||
};
|
||||
var acplAvg = ctrl.data.perfs[0].results.base.gameSections.all.acpl.avg;
|
||||
return m('table.selector.slist', [
|
||||
thead(ctrl),
|
||||
m('tbody', ctrl.data.perfs.map(function(o, i) {
|
||||
var perf = o.perf;
|
||||
var perfResults = o.results;
|
||||
var results = perfResults.base;
|
||||
var acpl = results.gameSections.all.acpl.avg;
|
||||
return m('tr', {
|
||||
key: perf.key,
|
||||
onclick: function() {
|
||||
ctrl.inspect(perf.key);
|
||||
},
|
||||
class: (ctrl.vm.inspecting === perf.key ? 'active' : '')
|
||||
}, [
|
||||
m('td', [
|
||||
m('div.name.text', {
|
||||
'data-icon': perf.icon
|
||||
}, perf.name)
|
||||
]),
|
||||
m('td', [
|
||||
m('div', results.nbGames + ' (' + percent(results.nbGames) + '%)')
|
||||
]),
|
||||
m('td', coach.resultBar(results)),
|
||||
m('td', coach.shared.progress(results.nbGames > 0 ? results.ratingDiff / results.nbGames : 0)),
|
||||
m('td', [
|
||||
m('span.progress', acpl === null ? '-' : m('span', {
|
||||
class: acpl > acplAvg ? 'negative' : 'positive'
|
||||
}, acpl))
|
||||
]),
|
||||
m('td', [
|
||||
m('time.moment-from-now', {
|
||||
datetime: results.lastPlayed
|
||||
})
|
||||
])
|
||||
]);
|
||||
}))
|
||||
]);
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var inspect = require('./inspect');
|
||||
var table = require('./table');
|
||||
var Slider = require('coach').slider;
|
||||
var shared = require('coach').shared;
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
if (!ctrl.nbPeriods) return m('div.content_box_top', [
|
||||
m('h1', [
|
||||
shared.userLink(ctrl.user.name),
|
||||
' moves: No data available'
|
||||
]),
|
||||
]);
|
||||
return m('div', {
|
||||
config: function() {
|
||||
$('body').trigger('lichess.content_loaded');
|
||||
}
|
||||
}, [
|
||||
m('div.content_box_top', {
|
||||
class: 'content_box_top' + (ctrl.vm.loading ? ' loading' : '')
|
||||
}, [
|
||||
ctrl.nbPeriods > 1 ? m.component(Slider, {
|
||||
max: ctrl.nbPeriods,
|
||||
range: ctrl.vm.range,
|
||||
dates: ctrl.data ? [ctrl.data.from, ctrl.data.to] : null,
|
||||
onChange: ctrl.selectPeriodRange
|
||||
}) : null,
|
||||
m('h1', [
|
||||
shared.userLink(ctrl.user.name),
|
||||
' moves',
|
||||
ctrl.data ? m('div.over', [
|
||||
' over ',
|
||||
ctrl.data.perfs[0].results.base.nbGames,
|
||||
' games'
|
||||
]) : null
|
||||
]),
|
||||
]),
|
||||
ctrl.vm.preloading ? m('div.loader') : (!ctrl.data ? m('div.top.nodata', m('p', 'Empty period range!')) : [
|
||||
inspect(ctrl),
|
||||
table(ctrl)
|
||||
])
|
||||
]);
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "coach",
|
||||
"version": "1.0.0",
|
||||
"description": "lichess.org coach",
|
||||
"main": "src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ornicar/lila"
|
||||
},
|
||||
"keywords": [
|
||||
"chess",
|
||||
"lichess",
|
||||
"coach"
|
||||
],
|
||||
"author": "ornicar",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ornicar/lila/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ornicar/lila",
|
||||
"devDependencies": {
|
||||
"browserify": "~9.0.8",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-streamify": "~0.0.5",
|
||||
"gulp-uglify": "~1.2.0",
|
||||
"gulp-util": "~3.0.4",
|
||||
"vinyl-source-stream": "~1.1.0",
|
||||
"watchify": "~3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"mithril": "0.2.0"
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var shared = require('./shared');
|
||||
|
||||
module.exports = {
|
||||
throttle: require('./throttle'),
|
||||
slider: require('./slider'),
|
||||
shared: shared,
|
||||
bestWin: function(w, color) {
|
||||
if (!w.user) return;
|
||||
return m('a', {
|
||||
href: '/' + w.id + '/' + color
|
||||
}, [
|
||||
w.user.title ? (w.user.title + ' ') : '',
|
||||
w.user.name,
|
||||
' (',
|
||||
m('strong', w.rating),
|
||||
')'
|
||||
]);
|
||||
},
|
||||
resultBar: function(r) {
|
||||
return m('div.result-bar', [
|
||||
['nbWin', 'win'],
|
||||
['nbDraw', 'draw'],
|
||||
['nbLoss', 'loss']
|
||||
].map(function(x) {
|
||||
var k = x[0];
|
||||
var name = x[1];
|
||||
var percent = (r[k] * 100 / r.nbGames);
|
||||
return m('div', {
|
||||
key: k,
|
||||
class: k,
|
||||
style: {
|
||||
width: percent + '%'
|
||||
}
|
||||
}, [
|
||||
m('strong', Math.round(percent)),
|
||||
'% ' + name
|
||||
]);
|
||||
}));
|
||||
},
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var strings = {
|
||||
acpl: 'Average centipawn loss per move',
|
||||
ratingDiff: 'Average rating points won',
|
||||
result: 'Wins, draws and losses'
|
||||
};
|
||||
|
||||
function decimals(nb) {
|
||||
return Number(nb).toFixed(2);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
strings: strings,
|
||||
userLink: function(username) {
|
||||
return m('a', {
|
||||
href: '/@/' + username
|
||||
}, username);
|
||||
},
|
||||
momentFromNow: function(date) {
|
||||
return m('time.moment-from-now', {
|
||||
datetime: date
|
||||
});
|
||||
},
|
||||
momentFormat: function(date, format) {
|
||||
var parsed = moment(date);
|
||||
var textContent = (format || 'calendar') === 'calendar' ? parsed.calendar() : parsed.format(format);
|
||||
return m('time', {
|
||||
datetime: date
|
||||
}, format == 'calendar' ? parsed.calendar() : parsed.format(format));
|
||||
},
|
||||
progress: function(r) {
|
||||
var perf;
|
||||
var dec = decimals(r > 0 ? r : -r);
|
||||
if (r === 0) perf = m('span', ' =');
|
||||
else if (r > 0) perf = m('span.positive[data-icon=N]', dec);
|
||||
else if (r < 0) perf = m('span.negative[data-icon=M]', dec);
|
||||
return m('span.rating.progress.hint--top', {
|
||||
'data-hint': strings.ratingDiff
|
||||
}, perf);
|
||||
},
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var momentFormat = require('./shared').momentFormat;
|
||||
|
||||
var cubeFacets = ['a', 'b', 'c', 'd'].map(function(x) {
|
||||
return m('div.' + x, m('div'));
|
||||
});
|
||||
|
||||
var dateFormat = 'LL';
|
||||
|
||||
module.exports = {
|
||||
controller: function(args) {
|
||||
this.max = args.max;
|
||||
this.range = args.range;
|
||||
},
|
||||
view: function(ctrl, args) {
|
||||
var ratio = function(x) {
|
||||
return x * 100 / ctrl.max;
|
||||
};
|
||||
return m('div.cube', [
|
||||
cubeFacets,
|
||||
m('div.slider', {
|
||||
config: function(el, isUpdate) {
|
||||
if (isUpdate) return;
|
||||
$(el).slider({
|
||||
range: true,
|
||||
min: 0,
|
||||
max: ctrl.max,
|
||||
values: ctrl.range,
|
||||
slide: function(event, ui) {
|
||||
$(el).parent().find('.a div, .b div, .c div, .d div').css({
|
||||
width: ratio(ui.values[1] - ui.values[0]) + "%",
|
||||
marginLeft: ratio(ui.values[0]) + '%'
|
||||
});
|
||||
args.onChange(ui.values[0], ui.values[1]);
|
||||
}
|
||||
}).find('.ui-slider-handle').text('<>');
|
||||
}
|
||||
}),
|
||||
args.dates ? m('div.dates', [
|
||||
'From ',
|
||||
momentFormat(args.dates[0], dateFormat),
|
||||
' to ',
|
||||
momentFormat(args.dates[1], dateFormat)
|
||||
]) : null
|
||||
]);
|
||||
}
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
module.exports = {
|
||||
acpl: 'Average centipawn loss per move',
|
||||
ratingDiff: 'Average rating points won',
|
||||
result: 'Wins, draws and losses'
|
||||
};
|
|
@ -1,86 +0,0 @@
|
|||
/**
|
||||
* https://github.com/niksy/throttle-debounce/blob/master/lib/throttle.js
|
||||
*
|
||||
* Throttle execution of a function. Especially useful for rate limiting
|
||||
* execution of handlers on events like resize and scroll.
|
||||
*
|
||||
* @param {Number} delay A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
|
||||
* @param {Boolean} noTrailing Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds while the
|
||||
* throttled-function is being called. If noTrailing is false or unspecified, callback will be executed one final time
|
||||
* after the last throttled-function call. (After the throttled-function has not been called for `delay` milliseconds,
|
||||
* the internal counter is reset)
|
||||
* @param {Function} callback A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
|
||||
* to `callback` when the throttled-function is executed.
|
||||
* @param {Boolean} debounceMode If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end),
|
||||
* schedule `callback` to execute after `delay` ms.
|
||||
*
|
||||
* @return {Function} A new, throttled, function.
|
||||
*/
|
||||
module.exports = function(delay, noTrailing, callback, debounceMode) {
|
||||
|
||||
// After wrapper has stopped being called, this timeout ensures that
|
||||
// `callback` is executed at the proper times in `throttle` and `end`
|
||||
// debounce modes.
|
||||
var timeoutID;
|
||||
|
||||
// Keep track of the last time `callback` was executed.
|
||||
var lastExec = 0;
|
||||
|
||||
// `noTrailing` defaults to falsy.
|
||||
if (typeof(noTrailing) !== 'boolean') {
|
||||
debounceMode = callback;
|
||||
callback = noTrailing;
|
||||
noTrailing = undefined;
|
||||
}
|
||||
|
||||
// The `wrapper` function encapsulates all of the throttling / debouncing
|
||||
// functionality and when executed will limit the rate at which `callback`
|
||||
// is executed.
|
||||
return function() {
|
||||
|
||||
var self = this;
|
||||
var elapsed = Number(new Date()) - lastExec;
|
||||
var args = arguments;
|
||||
|
||||
// Execute `callback` and update the `lastExec` timestamp.
|
||||
function exec() {
|
||||
lastExec = Number(new Date());
|
||||
callback.apply(self, args);
|
||||
}
|
||||
|
||||
// If `debounceMode` is true (at begin) this is used to clear the flag
|
||||
// to allow future `callback` executions.
|
||||
function clear() {
|
||||
timeoutID = undefined;
|
||||
}
|
||||
|
||||
if (debounceMode && !timeoutID) {
|
||||
// Since `wrapper` is being called for the first time and
|
||||
// `debounceMode` is true (at begin), execute `callback`.
|
||||
exec();
|
||||
}
|
||||
|
||||
// Clear any existing timeout.
|
||||
if (timeoutID) {
|
||||
clearTimeout(timeoutID);
|
||||
}
|
||||
|
||||
if (debounceMode === undefined && elapsed > delay) {
|
||||
// In throttle mode, if `delay` time has been exceeded, execute
|
||||
// `callback`.
|
||||
exec();
|
||||
|
||||
} else if (noTrailing !== true) {
|
||||
// In trailing throttle mode, since `delay` time has not been
|
||||
// exceeded, schedule `callback` to execute `delay` ms after most
|
||||
// recent execution.
|
||||
//
|
||||
// If `debounceMode` is true (at begin), schedule `clear` to execute
|
||||
// after `delay` ms.
|
||||
//
|
||||
// If `debounceMode` is false (at end), schedule `callback` to
|
||||
// execute after `delay` ms.
|
||||
timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
var source = require('vinyl-source-stream');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var watchify = require('watchify');
|
||||
var browserify = require('browserify');
|
||||
var uglify = require('gulp-uglify');
|
||||
var streamify = require('gulp-streamify');
|
||||
|
||||
var sources = ['./src/main.js'];
|
||||
var destination = '../../public/compiled/';
|
||||
var onError = function(error) {
|
||||
gutil.log(gutil.colors.red(error.message));
|
||||
};
|
||||
var standalone = 'LichessCoachOpening';
|
||||
|
||||
gulp.task('prod', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.opening.min.js'))
|
||||
.pipe(streamify(uglify()))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('dev', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.opening.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
var opts = watchify.args;
|
||||
opts.debug = true;
|
||||
opts.standalone = standalone;
|
||||
|
||||
var bundleStream = watchify(browserify(sources, opts))
|
||||
.on('update', rebundle)
|
||||
.on('log', gutil.log);
|
||||
|
||||
function rebundle() {
|
||||
return bundleStream.bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.opening.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
}
|
||||
|
||||
return rebundle();
|
||||
});
|
||||
|
||||
gulp.task('default', ['watch']);
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"name": "coachOpening",
|
||||
"version": "1.0.0",
|
||||
"description": "lichess.org opening coach",
|
||||
"main": "src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ornicar/lila"
|
||||
},
|
||||
"keywords": [
|
||||
"chess",
|
||||
"lichess",
|
||||
"coach",
|
||||
"opening"
|
||||
],
|
||||
"author": "ornicar",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ornicar/lila/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ornicar/lila",
|
||||
"devDependencies": {
|
||||
"browserify": "~9.0.8",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-streamify": "~0.0.5",
|
||||
"gulp-uglify": "~1.2.0",
|
||||
"gulp-util": "~3.0.4",
|
||||
"vinyl-source-stream": "~1.1.0",
|
||||
"watchify": "~3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chessground": "github:ornicar/chessground#v3.0.4",
|
||||
"coach": "../coach",
|
||||
"mithril": "github:ornicar/mithril.js#v1.0.0"
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var chessground = require('chessground');
|
||||
var throttle = require('coach').throttle;
|
||||
|
||||
function copy(obj, newValues) {
|
||||
var k, c = {};
|
||||
for (k in obj) {
|
||||
c[k] = obj[k];
|
||||
}
|
||||
for (k in newValues) {
|
||||
c[k] = newValues[k];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
module.exports = function(opts) {
|
||||
|
||||
this.user = opts.user;
|
||||
this.color = opts.color;
|
||||
this.nbPeriods = opts.nbPeriods;
|
||||
|
||||
this.vm = {
|
||||
preloading: !!this.nbPeriods,
|
||||
loading: true,
|
||||
range: [0, this.nbPeriods],
|
||||
sort: {
|
||||
prop: 'nbGames',
|
||||
order: -1
|
||||
},
|
||||
inspecting: null,
|
||||
/* {
|
||||
eco: 'D00',
|
||||
chessground: null
|
||||
} */
|
||||
};
|
||||
|
||||
var requestData = throttle(1000, false, function() {
|
||||
m.request({
|
||||
url: '/coach/opening/' + this.user.id + '/' + this.color + '.json',
|
||||
data: {
|
||||
range: this.vm.range.join('-')
|
||||
}
|
||||
}).then(function(data) {
|
||||
|
||||
this.data = data;
|
||||
|
||||
this.list = Object.keys(this.data.openings).map(function(eco) {
|
||||
var o = this.data.openings[eco];
|
||||
var r = o.results;
|
||||
return copy(o, {
|
||||
result: r.nbWin / r.nbLoss,
|
||||
acpl: r.gameSections.all.acpl.avg,
|
||||
ratingDiffAvg: r.nbGames > 0 ? r.ratingDiff / r.nbGames : 0,
|
||||
// just for sorting
|
||||
name: o.opening.name,
|
||||
nbGames: r.nbGames,
|
||||
lastPlayed: r.lastPlayed
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
this.sortList();
|
||||
|
||||
if (location.hash) this.inspect(location.hash.replace(/#/, '').replace(/_/g, ' '));
|
||||
|
||||
this.vm.preloading = false;
|
||||
this.vm.loading = false;
|
||||
m.redraw();
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
if (this.nbPeriods) setTimeout(requestData, 200);
|
||||
|
||||
this.selectPeriodRange = function(from, to) {
|
||||
this.vm.range = [from, to];
|
||||
this.vm.loading = true;
|
||||
if (from === to) this.data = null;
|
||||
else requestData();
|
||||
m.redraw();
|
||||
}.bind(this);
|
||||
|
||||
this.sortList = function() {
|
||||
var s = this.vm.sort;
|
||||
this.list.sort(function(a, b) {
|
||||
return a[s.prop] > b[s.prop] ? s.order : a[s.prop] < b[s.prop] ? -s.order : 0;
|
||||
});
|
||||
}.bind(this);
|
||||
|
||||
this.setSort = function(prop) {
|
||||
if (this.vm.sort.prop === prop) this.vm.sort.order = -this.vm.sort.order;
|
||||
else this.vm.sort = {
|
||||
prop: prop,
|
||||
order: 1
|
||||
};
|
||||
this.sortList();
|
||||
}.bind(this);
|
||||
|
||||
this.jumpBy = function(delta) {
|
||||
if (!this.vm.inspecting) return;
|
||||
var ecos = this.list.map(function(o) {
|
||||
return o.opening.eco;
|
||||
});
|
||||
var i = ecos.indexOf(this.vm.inspecting.eco);
|
||||
var i2 = (i + delta) % ecos.length;
|
||||
if (i2 < 0) i2 = ecos.length - 1;
|
||||
this.inspect(ecos[i2]);
|
||||
}.bind(this);
|
||||
|
||||
this.isInspecting = function(eco) {
|
||||
return this.vm.inspecting && this.vm.inspecting.eco === eco;
|
||||
}.bind(this);
|
||||
|
||||
this.inspect = function(eco) {
|
||||
if (!this.data.openings[eco]) return;
|
||||
if (this.isInspecting(eco)) return;
|
||||
if (window.history.replaceState)
|
||||
window.history.replaceState(null, null, '#' + eco);
|
||||
var opening = this.data.openings[eco].opening;
|
||||
var config = {
|
||||
fen: opening.fen,
|
||||
orientation: this.color,
|
||||
viewOnly: true,
|
||||
minimalDom: true,
|
||||
lastMove: opening.lastMoveUci ? [opening.lastMoveUci.substr(0, 2), opening.lastMoveUci.substr(2, 2)] : null,
|
||||
coordinates: false
|
||||
};
|
||||
if (this.vm.inspecting) {
|
||||
this.vm.inspecting.eco = eco;
|
||||
this.vm.inspecting.chessground.set(config);
|
||||
} else
|
||||
this.vm.inspecting = {
|
||||
eco: eco,
|
||||
chessground: new chessground.controller(config)
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
this.uninspect = function() {
|
||||
this.vm.inspecting = null;
|
||||
if (window.history.replaceState)
|
||||
window.history.replaceState(null, null, '#');
|
||||
}.bind(this);
|
||||
|
||||
this.trans = lichess.trans(opts.i18n);
|
||||
};
|
|
@ -1,99 +0,0 @@
|
|||
var m = require('mithril');
|
||||
var chessground = require('chessground');
|
||||
|
||||
var sections = require('./sections');
|
||||
var coach = require('coach');
|
||||
|
||||
function sideCommands(ctrl) {
|
||||
return [
|
||||
m('a.to.back', {
|
||||
'data-icon': 'L',
|
||||
onclick: ctrl.uninspect
|
||||
}),
|
||||
m('a.to.prev', {
|
||||
'data-icon': 'I',
|
||||
onclick: function() {
|
||||
ctrl.jumpBy(-1);
|
||||
}
|
||||
}),
|
||||
m('a.to.next', {
|
||||
'data-icon': 'H',
|
||||
onclick: function() {
|
||||
ctrl.jumpBy(1);
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = function(ctrl, inspecting) {
|
||||
var d = ctrl.data;
|
||||
var eco = inspecting.eco;
|
||||
var o = d.openings[eco];
|
||||
if (!o) return m('div.top.nodata', [
|
||||
sideCommands(ctrl),
|
||||
m('p', 'No results for this data range and opening!')
|
||||
]);
|
||||
var opening = o.opening;
|
||||
var results = o.results;
|
||||
var user = ctrl.user.id;
|
||||
var searchUrl = '/games/search?players.a=' + user + '&players.' + ctrl.color + '=' + user + '&opening=' + eco;
|
||||
var analysedUrl = searchUrl + '&analysed=1';
|
||||
return m('div.top.inspect', [
|
||||
sideCommands(ctrl),
|
||||
coach.resultBar(results),
|
||||
m('div.main', [
|
||||
coach.shared.progress(results.ratingDiff / results.nbGames),
|
||||
m('h2', [
|
||||
m('strong', [
|
||||
opening.eco,
|
||||
' ',
|
||||
opening.name
|
||||
]),
|
||||
m('em', opening.moves)
|
||||
]),
|
||||
m('div.baseline', [
|
||||
m('a', {
|
||||
href: searchUrl
|
||||
}, [
|
||||
m('strong', results.nbGames),
|
||||
' games'
|
||||
]),
|
||||
', ',
|
||||
m('a', {
|
||||
href: analysedUrl
|
||||
}, [
|
||||
m('strong', results.nbAnalysis),
|
||||
' analysed.'
|
||||
]),
|
||||
' Last played ',
|
||||
coach.shared.momentFromNow(results.lastPlayed),
|
||||
'.',
|
||||
])
|
||||
]),
|
||||
m('div.content', [
|
||||
m('div.board',
|
||||
chessground.view(ctrl.vm.inspecting.chessground)
|
||||
),
|
||||
m('div.right', [
|
||||
// moves(ctrl, results),
|
||||
sections(ctrl, results),
|
||||
results.bestWin ? [
|
||||
m('br'),
|
||||
' Best win: ',
|
||||
coach.bestWin(results.bestWin, ctrl.color)
|
||||
] : null
|
||||
// m('table', [
|
||||
// m('tr', [
|
||||
// m('tr', [
|
||||
// m('th', 'Average opponent'),
|
||||
// m('tr', m('strong', results.opponentRatingAvg))
|
||||
// ]),
|
||||
// results.bestWin ? m('tr', [
|
||||
// m('th', 'Best win'),
|
||||
// m('tr', bestWin(results.bestWin))
|
||||
// ]) : null
|
||||
// ])
|
||||
])
|
||||
])
|
||||
]);
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var ctrl = require('./ctrl');
|
||||
var view = require('./view');
|
||||
|
||||
module.exports = function(element, opts) {
|
||||
|
||||
var controller = new ctrl(opts);
|
||||
|
||||
m.module(element, {
|
||||
controller: function() {
|
||||
return controller;
|
||||
},
|
||||
view: view
|
||||
});
|
||||
};
|
|
@ -1,133 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var green = '#759900',
|
||||
red = '#dc322f',
|
||||
orange = '#d59120',
|
||||
grey = '#aaaaaa';
|
||||
|
||||
var MAX_MOVES = 30;
|
||||
|
||||
function makeChart(el, data) {
|
||||
$(el).highcharts({
|
||||
chart: {
|
||||
spacing: [0, 0, 0, 0],
|
||||
animation: {
|
||||
duration: 300
|
||||
},
|
||||
backgroundColor: null,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
plotBackgroundColor: null,
|
||||
plotShadow: false,
|
||||
plotBorderWidth: 0
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
xAxis: {
|
||||
crosshair: false
|
||||
},
|
||||
yAxis: [{
|
||||
min: 0,
|
||||
tickInterval: 50,
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
labels: {
|
||||
enabled: false
|
||||
},
|
||||
lineWidth: 1,
|
||||
gridLineWidth: 1
|
||||
}, {
|
||||
min: 0,
|
||||
tickInterval: 10,
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
labels: {
|
||||
enabled: false
|
||||
},
|
||||
lineWidth: 1,
|
||||
gridLineWidth: 1
|
||||
}],
|
||||
plotOptions: {
|
||||
column: {
|
||||
pointPadding: 0.2,
|
||||
borderWidth: 0
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
useHTML: true,
|
||||
formatter: function() {
|
||||
if (this.point.acpl)
|
||||
return '[move ' + this.point.x + '] <b>' + this.point.acpl.avg + '</b> centipawns<hr>' +
|
||||
'over ' + this.point.acpl.nb + ' analysed games';
|
||||
return '[move ' + this.point.x + '] <b>' + (this.point.time.avg / 10) + '</b> seconds<hr>' +
|
||||
'over ' + this.point.time.nb + ' games';
|
||||
},
|
||||
},
|
||||
series: [{
|
||||
name: 'ACPL',
|
||||
type: 'column',
|
||||
data: data.acpls,
|
||||
pointWidth: 12
|
||||
|
||||
}, {
|
||||
name: 'Time',
|
||||
data: data.times,
|
||||
type: 'spline',
|
||||
yAxis: 1,
|
||||
lineWidth: 1,
|
||||
marker: {
|
||||
radius: 2
|
||||
}
|
||||
}],
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
return $(el).highcharts();
|
||||
}
|
||||
|
||||
function makeData(results, moves) {
|
||||
return {
|
||||
acpls: results.nbAnalysis ? moves.map(function(move, i) {
|
||||
var acpl = move.acpl.avg;
|
||||
return {
|
||||
x: i + 1,
|
||||
y: Math.min(acpl, 150) + 10,
|
||||
color: acpl < 50 ? green : (acpl < 100 ? orange : red),
|
||||
acpl: move.acpl
|
||||
};
|
||||
}) : [],
|
||||
times: moves.map(function(move, i) {
|
||||
var time = move.time.avg;
|
||||
return {
|
||||
x: i + 1,
|
||||
y: Math.min(time, 30) + 1,
|
||||
time: move.time
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(ctrl, results) {
|
||||
var moves = results.moves[ctrl.data.color].slice(0, MAX_MOVES);
|
||||
var acpl = results.gameSections.all.acpl.avg;
|
||||
var globalAcpl = ctrl.data.colorResults.gameSections.all.acpl.avg;
|
||||
return [
|
||||
m('div.moves', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
var data = makeData(results, moves);
|
||||
if (ctx.chart) {
|
||||
ctx.chart.series[0].setData(data.acpls)
|
||||
ctx.chart.series[1].setData(data.times)
|
||||
}
|
||||
else ctx.chart = makeChart(el, data);
|
||||
}
|
||||
})
|
||||
];
|
||||
};
|
|
@ -1,167 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
function sortByY(arr) {
|
||||
return arr.sort(function(a, b) {
|
||||
return a.y < b.y;
|
||||
});
|
||||
}
|
||||
|
||||
function signed(i) {
|
||||
return (i > 0 ? '+' : '') + i;
|
||||
}
|
||||
|
||||
function makeSeries(ctrl) {
|
||||
var data = ctrl.data;
|
||||
var percent = function(nb) {
|
||||
return nb * 100 / data.openingResults.nbGames;
|
||||
};
|
||||
var colors = Highcharts.getOptions().colors,
|
||||
raw = data.families.sort(function(a, b) {
|
||||
return a.results.nbGames < b.results.nbGames ? 1 : -1;
|
||||
}).map(function(fam, index) {
|
||||
var graphColor = colors[index % colors.length];
|
||||
var family = fam.family;
|
||||
var results = fam.results;
|
||||
var openings = family.ecos.map(function(eco) {
|
||||
return data.openings[eco];
|
||||
}).sort(function(a, b) {
|
||||
return a.results.nbGames < b.results.nbGames ? 1 : -1;
|
||||
});
|
||||
return {
|
||||
name: family.name,
|
||||
y: percent(results.nbGames),
|
||||
results: results,
|
||||
color: graphColor,
|
||||
drilldown: {
|
||||
name: family.name,
|
||||
data: openings.map(function(o) {
|
||||
return {
|
||||
name: o.opening.eco + ' ' + o.opening.name,
|
||||
y: percent(o.results.nbGames),
|
||||
opening: o.opening,
|
||||
results: o.results
|
||||
};
|
||||
}),
|
||||
color: graphColor
|
||||
}
|
||||
};
|
||||
}),
|
||||
familyData = [],
|
||||
openingData = [],
|
||||
i,
|
||||
j,
|
||||
drillDataLen;
|
||||
|
||||
// Build the data arrays
|
||||
for (i = 0; i < raw.length; i += 1) {
|
||||
|
||||
familyData.push({
|
||||
name: raw[i].name,
|
||||
y: raw[i].y,
|
||||
results: raw[i].results,
|
||||
color: raw[i].color
|
||||
});
|
||||
|
||||
drillDataLen = raw[i].drilldown.data.length;
|
||||
for (j = 0; j < drillDataLen; j += 1) {
|
||||
var d = raw[i].drilldown.data[j];
|
||||
d.color = Highcharts.Color(raw[i].color).brighten(0.2 - (j / drillDataLen) / 3).get()
|
||||
openingData.push(d);
|
||||
}
|
||||
}
|
||||
return [familyData, openingData];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
update: function(chart, ctrl) {
|
||||
var series = makeSeries(ctrl);
|
||||
chart.series[0].setData(series[0]);
|
||||
chart.series[1].setData(series[1]);
|
||||
},
|
||||
create: function(el, ctrl) {
|
||||
var series = makeSeries(ctrl);
|
||||
|
||||
// Create the chart
|
||||
$(el).highcharts({
|
||||
chart: {
|
||||
type: 'pie',
|
||||
animation: {
|
||||
duration: 300
|
||||
},
|
||||
backgroundColor: null,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
plotBackgroundColor: null,
|
||||
plotShadow: false,
|
||||
plotBorderWidth: 0
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
yAxis: {},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
shadow: false,
|
||||
center: ['50%', '50%'],
|
||||
animation: false,
|
||||
point: {}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
useHTML: true,
|
||||
headerFormat: '{point.key}',
|
||||
pointFormatter: function() {
|
||||
var o = this.opening;
|
||||
var r = this.results;
|
||||
var acpl = r.gameSections.all.acpl.avg;
|
||||
return ((o && o.formattedMoves) ? ('<br>' + o.formattedMoves + '<br><br>') : '') + '<table><tr><td>Games:</td><td style="text-align: right"><b>' +
|
||||
r.nbGames + '</b></td></tr>' +
|
||||
'<tr><td>Rating:</td><td style="text-align: right"><b>' +
|
||||
signed(r.ratingDiff) + '</b></td></tr>' +
|
||||
'<tr><td>ACPL:</td><td style="text-align: right"><b>' +
|
||||
(acpl === null ? '?' : acpl) + '</b></td></tr></table>';
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'First move',
|
||||
data: series[0],
|
||||
size: '60%',
|
||||
dataLabels: {
|
||||
formatter: function() {
|
||||
return this.y > 3 ? this.point.name : null;
|
||||
},
|
||||
color: 'white',
|
||||
distance: -30,
|
||||
},
|
||||
}, {
|
||||
name: 'Openings',
|
||||
data: series[1],
|
||||
size: '90%',
|
||||
innerSize: '60%',
|
||||
dataLabels: {
|
||||
formatter: function() {
|
||||
return this.y > 1 ? this.point.name + ': ' + Math.round(this.y) + '%' : null;
|
||||
}
|
||||
},
|
||||
cursor: 'pointer',
|
||||
point: {
|
||||
events: {
|
||||
click: function(e) {
|
||||
if (e.point) {
|
||||
ctrl.inspect(e.point.opening.eco);
|
||||
m.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
return $(el).highcharts();
|
||||
}
|
||||
};
|
|
@ -1,121 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var green = '#759900',
|
||||
red = '#dc322f',
|
||||
orange = '#d59120',
|
||||
translucid = 'rgba(0,0,0,0.3)';
|
||||
|
||||
function makeChart(el, data) {
|
||||
$(el).highcharts({
|
||||
chart: {
|
||||
type: 'column',
|
||||
spacing: [0, 0, 0, 0],
|
||||
animation: {
|
||||
duration: 300
|
||||
},
|
||||
backgroundColor: null,
|
||||
borderWidth: 0,
|
||||
borderRadius: 0,
|
||||
plotBackgroundColor: null,
|
||||
plotShadow: false,
|
||||
plotBorderWidth: 0
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
xAxis: {
|
||||
tickWidth: 0,
|
||||
categories: Object.keys(sectionNames).map(function(k) {
|
||||
return sectionNames[k];
|
||||
})
|
||||
},
|
||||
yAxis: {
|
||||
min: 0,
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
labels: {
|
||||
enabled: false
|
||||
},
|
||||
lineWidth: 0,
|
||||
gridLineWidth: 0
|
||||
},
|
||||
plotOptions: {
|
||||
column: {
|
||||
pointPadding: 0,
|
||||
borderWidth: 0
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
useHTML: true,
|
||||
formatter: function() {
|
||||
return this.point.name + '<b>' + this.point.acpl.avg + '</b> centipawns<hr>' +
|
||||
'over ' + this.point.acpl.nb + ' analysed games';
|
||||
},
|
||||
},
|
||||
series: [{
|
||||
name: 'ACPL',
|
||||
data: data.acpls,
|
||||
pointWidth: 80,
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
format: '{point.y} ACPL'
|
||||
}
|
||||
}, {
|
||||
name: 'Global',
|
||||
data: data.globals,
|
||||
color: 'rgba(0,0,0,.3)',
|
||||
pointWidth: 8,
|
||||
pointPlacement: 0.14
|
||||
}],
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
return $(el).highcharts();
|
||||
}
|
||||
|
||||
var sectionKeys = ['opening', 'middle', 'end', 'all'];
|
||||
var sectionNames = {
|
||||
opening: 'Opening',
|
||||
middle: 'Middlegame',
|
||||
end: 'Endgame',
|
||||
all: 'Overall'
|
||||
};
|
||||
|
||||
function makeData(sections, isGlobal) {
|
||||
return sectionKeys.map(function(key, i) {
|
||||
var acpl = sections[key].acpl.avg;
|
||||
return {
|
||||
y: Math.min(acpl, 150) + 10,
|
||||
color: isGlobal ? translucid : (acpl < 50 ? green : (acpl < 100 ? orange : red)),
|
||||
acpl: sections[key].acpl,
|
||||
name: isGlobal ? '[average] ' : ''
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(ctrl, results) {
|
||||
if (!results.nbAnalysis)
|
||||
return m('div.not_analysed', 'No analysis available on these games!')
|
||||
var sections = results.gameSections;
|
||||
var global = ctrl.data.openingResults.gameSections;
|
||||
return [
|
||||
results.nbAnalysis ? m('h3', 'ACPL (Average centipawns lost) per section:') : null,
|
||||
m('div.sections', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
var data = {
|
||||
acpls: makeData(sections),
|
||||
globals: makeData(global, true)
|
||||
};
|
||||
if (ctx.chart) {
|
||||
ctx.chart.series[0].setData(data.acpls)
|
||||
ctx.chart.series[1].setData(data.globals)
|
||||
} else ctx.chart = makeChart(el, data);
|
||||
}
|
||||
})
|
||||
];
|
||||
};
|
|
@ -1,74 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var coach = require('coach');
|
||||
var strings = coach.shared.strings;
|
||||
|
||||
var headers = [
|
||||
['name', 'Opening'],
|
||||
['nbGames', 'Games'],
|
||||
['result', 'Result', strings.result],
|
||||
['ratingDiffAvg', 'Rating', strings.ratingDiff],
|
||||
['acpl', 'ACPL', strings.acpl],
|
||||
['lastPlayed', 'Last played']
|
||||
];
|
||||
|
||||
function thead(list, ctrl) {
|
||||
return m('thead', {
|
||||
onclick: function(e) {
|
||||
var prop = e.target.getAttribute("data-sort-by") || e.target.parentNode.getAttribute("data-sort-by");
|
||||
if (prop) ctrl.setSort(prop);
|
||||
}
|
||||
}, m('tr', headers.map(function(h) {
|
||||
var props = {
|
||||
key: h[0],
|
||||
'data-sort-by': h[0]
|
||||
};
|
||||
if (ctrl.vm.sort.prop === h[0]) props['data-icon'] = ctrl.vm.sort.order === -1 ? 'R' : 'S';
|
||||
var spanProps = {};
|
||||
if (h[2]) {
|
||||
spanProps.class = 'hint--top';
|
||||
spanProps['data-hint'] = h[2];
|
||||
}
|
||||
return m('th', props, m('span', spanProps, h[1]));
|
||||
})));
|
||||
}
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
var d = ctrl.data;
|
||||
var percent = function(nb) {
|
||||
return Math.round(nb * 100 / d.openingResults.nbGames);
|
||||
};
|
||||
var acplAvg = ctrl.data.colorResults.gameSections.all.acpl.avg;
|
||||
return m('table.selector.slist', [
|
||||
thead(ctrl.list, ctrl),
|
||||
m('tbody', ctrl.list.map(function(o, i) {
|
||||
return m('tr', {
|
||||
key: o.opening.eco,
|
||||
onclick: function() {
|
||||
ctrl.inspect(o.opening.eco);
|
||||
},
|
||||
class: (ctrl.isInspecting(o.opening.eco) ? 'active' : '')
|
||||
}, [
|
||||
m('td', [
|
||||
m('div.name', o.opening.name),
|
||||
m('div.moves', o.opening.formattedMoves)
|
||||
]),
|
||||
m('td', [
|
||||
m('div', o.results.nbGames + ' (' + percent(o.results.nbGames) + '%)')
|
||||
]),
|
||||
m('td', coach.resultBar(o.results)),
|
||||
m('td', coach.shared.progress(o.ratingDiffAvg)),
|
||||
m('td', [
|
||||
m('span.progress', o.acpl === null ? '-' : m('span', {
|
||||
class: o.acpl > acplAvg ? 'negative' : 'positive'
|
||||
}, o.acpl))
|
||||
]),
|
||||
m('td', [
|
||||
m('time.moment-from-now', {
|
||||
datetime: o.results.lastPlayed
|
||||
})
|
||||
])
|
||||
]);
|
||||
}))
|
||||
]);
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var piechart = require('./piechart');
|
||||
var table = require('./table');
|
||||
var inspect = require('./inspect');
|
||||
var Slider = require('coach').slider;
|
||||
var shared = require('coach').shared;
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
if (!ctrl.nbPeriods) return m('div.content_box_top', [
|
||||
m('h1', [
|
||||
shared.userLink(ctrl.user.name),
|
||||
' openings as ',
|
||||
ctrl.color,
|
||||
': ',
|
||||
'No data available'
|
||||
]),
|
||||
]);
|
||||
return m('div', {
|
||||
config: function() {
|
||||
$('body').trigger('lichess.content_loaded');
|
||||
}
|
||||
}, [
|
||||
m('div.content_box_top', {
|
||||
class: 'content_box_top' + (ctrl.vm.loading ? ' loading' : '')
|
||||
}, [
|
||||
ctrl.nbPeriods > 1 ? m.component(Slider, {
|
||||
max: ctrl.nbPeriods,
|
||||
range: ctrl.vm.range,
|
||||
dates: ctrl.data ? [ctrl.data.from, ctrl.data.to] : null,
|
||||
onChange: ctrl.selectPeriodRange
|
||||
}) : null,
|
||||
m('h1', [
|
||||
shared.userLink(ctrl.user.name),
|
||||
' openings as ',
|
||||
ctrl.color,
|
||||
ctrl.data ? m('div.over', [
|
||||
' over ',
|
||||
ctrl.data.colorResults.nbGames,
|
||||
' games'
|
||||
]) : null
|
||||
]),
|
||||
]),
|
||||
ctrl.vm.preloading ? m('div.loader') : (!ctrl.data ? m('div.top.nodata', m('p', 'Empty period range!')) : [
|
||||
ctrl.vm.inspecting ? inspect(ctrl, ctrl.vm.inspecting) : m('div.top.chart', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
if (ctx.chart) piechart.update(ctx.chart, ctrl);
|
||||
else ctx.chart = piechart.create(el, ctrl);
|
||||
}
|
||||
}),
|
||||
table(ctrl)
|
||||
])
|
||||
]);
|
||||
};
|
|
@ -11,14 +11,14 @@ var destination = '../../public/compiled/';
|
|||
var onError = function(error) {
|
||||
gutil.log(gutil.colors.red(error.message));
|
||||
};
|
||||
var standalone = 'LichessCoach';
|
||||
var standalone = 'LichessInsight';
|
||||
|
||||
gulp.task('prod', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.min.js'))
|
||||
.pipe(source('lichess.insight.min.js'))
|
||||
.pipe(streamify(uglify()))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ gulp.task('dev', function() {
|
|||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.js'))
|
||||
.pipe(source('lichess.insight.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
|
@ -44,7 +44,7 @@ gulp.task('watch', function() {
|
|||
function rebundle() {
|
||||
return bundleStream.bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.coach.js'))
|
||||
.pipe(source('lichess.insight.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "coach",
|
||||
"name": "insight",
|
||||
"version": "1.0.0",
|
||||
"description": "lichess.org coach deep chess insights",
|
||||
"description": "lichess.org chess insights",
|
||||
"main": "src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -10,7 +10,7 @@
|
|||
"keywords": [
|
||||
"chess",
|
||||
"lichess",
|
||||
"coach",
|
||||
"insight",
|
||||
"analysis"
|
||||
],
|
||||
"author": "ornicar",
|
|
@ -18,7 +18,7 @@ module.exports = function(env) {
|
|||
if (!this.validCombinationCurrent()) return;
|
||||
m.request({
|
||||
method: 'post',
|
||||
url: '/coach/data/' + this.userId,
|
||||
url: '/insights/data/' + this.userId,
|
||||
data: {
|
||||
metric: this.vm.metric.key,
|
||||
dimension: this.vm.dimension.key,
|
|
@ -12,10 +12,10 @@ module.exports = function(ctrl) {
|
|||
m('div.refresh', {
|
||||
config: function(e, isUpdate) {
|
||||
if (isUpdate) return;
|
||||
var $ref = $('.coach-stale');
|
||||
var $ref = $('.insight-stale');
|
||||
if ($ref.length) {
|
||||
$(e).append($ref.show());
|
||||
lichess.refreshCoachForm();
|
||||
lichess.refreshInsightForm();
|
||||
}
|
||||
}
|
||||
})
|
Loading…
Reference in New Issue