Skip to content

Commit

Permalink
Merge branch 'master' into relay
Browse files Browse the repository at this point in the history
* master: (3132 commits)
  more space between tournament lanes
  don't display notes/settings on tournament games - fixes lichess-org#622
  Revert "Add colors to clock bar, fixes lichess-org#130"
  always displayed finished official tournaments
  fix as translation unit
  lt "lietuvių kalba" translation lichess-org#14529. Author: mast3r. Finished updated Lithuanian translation. One question, though, if you see this: is the description on line 108 correct? Line says „decline invitation“, but the description talks about takeback.
  he "עִבְרִית" translation lichess-org#14528. Author: Firebrass11. Added variant ending, the final translation.
  rename best3Of function
  show 4x2 perftypes in powertip user preview
  fix display of rated thematic short tournaments
  allow user tournaments to reuse bottom free lanes
  extend tourney schedule view
  schedule hourly tournaments up to 6 hours in advance
  gently refactor tournament scheduler
  try harder not to overlap scheduled tournaments
  more tournament schedule tweaks and simplifications
  display notable finished tournaments
  more tournament schedule tweaks
  add janis AI server
  add dragscroll extension for tournamentSchedule
  ...
  • Loading branch information
ornicar committed Jun 22, 2015
2 parents ddc8df3 + 8f8d1cf commit f9e37fb
Show file tree
Hide file tree
Showing 1,075 changed files with 45,899 additions and 13,684 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
[submodule "submodules/boardcreator"]
path = submodules/boardcreator
url = https://github.com/clarkerubber/board-creator
[submodule "submodules/evaluator"]
path = submodules/evaluator
url = https://github.com/clarkerubber/engine-evaluator
[submodule "ui/chessli"]
path = ui/chessli
url = https://github.com/ornicar/chess.js
Expand Down
2 changes: 1 addition & 1 deletion doc/LICENSE → LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2012-2014 Thibault Duplessis
Copyright (c) 2012-2015 Thibault Duplessis

The MIT license

Expand Down
63 changes: 15 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
[lichess.org](http:https://lichess.org)
---------------------------------

<img src="https://raw.githubusercontent.com/ornicar/lila/master/public/images/homepage_light.1200.png" alt="lichess.org" />

It's a free online chess game focused on [realtime](http:https://lichess.org/games) and ease of use

It has a [search engine](http:https://lichess.org/games/search),
[computer analysis](http:https://lichess.org/analyse/ief49lif),
[tournaments](http:https://lichess.org/tournament),
[simuls](http:https://lichess.org/simul),
[forums](http:https://lichess.org/forum),
[teams](http:https://lichess.org/team),
[puzzles](http:https://lichess.org/training),
a weird [monitoring console](http:https://lichess.org/monitor),
and a [world map](http:https://map.lichess.org).
[tactic trainer](http:https://lichess.org/training),
[opening trainer](http:https://lichess.org/training/opening),
a [mobile app](http:https://lichess.org/mobile),
a [monitoring console](http:https://lichess.org/monitor),
and a [network world map](http:https://lichess.org/network).
The UI is available in [80 languages](http:https://lichess.org/translation/contribute) thanks to the community.

Lichess is written in [Scala 2.11](http:https://www.scala-lang.org/),
and relies on [Play 2.3](http:https://www.playframework.com/) for the routing, templating, and JSON.
Pure chess logic is contained in [scalachess](http:https://github.com/ornicar/scalachess) submodule.
The codebase is fully asynchronous, making heavy use of Scala Futures and [Akka 2 actors](http:https://akka.io).
Lichess talks to [Stockfish 5](http:https://stockfishchess.org/) using a [FSM Actor](https://github.com/ornicar/lila/blob/master/modules/ai/src/main/ActorFSM.scala) to handle AI moves and analysis.
It uses [MongoDB 2.4](http:https://mongodb.org) to store more than 30 million games, which are indexed by [elasticsearch](http:https://elasticsearch.org).
HTTP requests and websocket connections are proxied by [nginx 1.4](http:https://nginx.org).
New client-side features are written in [ClojureScript](https:https://github.com/ornicar/lila/tree/master/cljs/puzzle/src).
Lichess talks to [Stockfish](http:https://stockfishchess.org/) deployed in an AI cluster of donated servers.
It uses [MongoDB 2.6](http:https://mongodb.org) to store more than 68 million games, which are indexed by [elasticsearch](http:https://elasticsearch.org).
HTTP requests and websocket connections are proxied by [nginx 1.6](http:https://nginx.org).
Client-side is written in [mithril.js](http:https://lhorie.github.io/mithril/).
The [blog](http:https://lichess.org/blog) uses a free open content plan from [prismic.io](http:https://prismic.io).

Join us on #lichess IRC channel on freenode for more info.
See the roadmap on https://etherpad.mozilla.org/ep/pad/view/ro.3bIwxJwTQYW/latest.
Use [github issues](https://github.com/ornicar/lila/issues) for bug reports and feature requests.

Installation
------------
Expand Down Expand Up @@ -325,11 +330,9 @@ name | type | default | description

(1) All game statuses: https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala#L16-L25

### `GET /{id}/pgn` fetch one game PGN by ID
### `GET /game/export/{id}.pgn` fetch one game PGN by ID

```
> curl http:https://en.lichess.org/Qa7FJNk2/pgn
```
http:https://en.lichess.org/game/export/Qa7FJNk2.pgn

This returns the raw PGN for a game.

Expand All @@ -354,42 +357,6 @@ This returns the raw PGN for a game.
1. e4 e5 2. Nf3 Nc6 3. Bc4 { Italian Game, General } Qf6?! { (0.13 → 0.98) Inaccuracy. The best move was Nf6. } (3... Nf6 4. d3 Bc5 5. O-O O-O 6. Bg5 Be7 7. a3 d6 8. h3 a6) 4. d3 h6 5. Be3 d6 6. h3?! { (0.84 → 0.31) Inaccuracy. The best move was Nc3. } (6. Nc3 Be6 7. Nd5 Qd8 8. d4 exd4 9. Nxd4 Nxd4 10. Qxd4 c6 11. Nc3 Bxc4 12. Qxc4 Nf6 13. O-O Be7) 6... a6 7. Nbd2 Be6 8. Qe2 Bxc4 9. Nxc4 Nge7 10. a3 Nd4?! { (0.29 → 0.79) Inaccuracy. The best move was O-O-O. } (10... O-O-O 11. O-O g5 12. a4 Bg7 13. Bd2 Kb8 14. Rae1 Qe6 15. b4 f5 16. b5 fxe4) 11. Bxd4 exd4 12. O-O-O Nc6 13. Rhe1 O-O-O 14. e5 dxe5 15. Nfxe5 Nxe5 16. Qxe5 Qxe5? { (0.35 → 1.78) Mistake. The best move was Qxf2. } (16... Qxf2 17. Re2 Qf6 18. Qxf6 gxf6 19. Rf1 Bg7 20. Nd2 h5 21. Ne4 Rhe8 22. Ref2 Re5 23. b3 Rdd5 24. Kb2) 17. Nxe5 Rg8?! { (1.76 → 2.32) Inaccuracy. The best move was Rd7. } (17... Rd7 18. Nxd7 Kxd7 19. Re4 c5 20. c3 dxc3 21. bxc3 Bd6 22. Kc2 b5 23. a4 Ra8 24. d4 Kc6 25. dxc5) 18. Nxf7 Rd7? { (2.35 → Mate in 2) Checkmate is now unavoidable. The best move was Rd5. } (18... Rd5 19. Re8+ Kd7 20. Rde1 Bb4 21. Rxg8 Bxe1 22. Rxg7 Bxf2 23. Nxh6+ Kc6 24. Kd1 Rb5 25. b3 Rh5 26. Nf7) 19. Re8+ { Black resigns } 1-0
```

### `GET /api/puzzle/<id>` fetch one puzzle

```
> curl http:https://en.lichess.org/api/puzzle/23045
```

```javascript
{
"id": 16177,
"url": "http:https://lichess.org/training/16177", // URL of the puzzle
"color": "black", // color of the player
"position": "6NK/5k2/2r5/2n3PP/8/8/8/8 w - - 7 39", // FEN initial position
"solution": ["c6h6", "g5h6", "c5e6", "h8h7", "e6g5",
"h7h8", "f7f8", "h6h7", "g5f7"], // solution moves
"rating": 1799 // puzzle glicko2 rating
}
```

### `GET /api/puzzle/daily` fetch daily puzzle

```
> curl http:https://en.lichess.org/api/puzzle/daily
```

```javascript
{
"id": 16177,
"url": "http:https://lichess.org/training/16177", // URL of the puzzle
"color": "black", // color of the player
"position": "6NK/5k2/2r5/2n3PP/8/8/8/8 w - - 7 39", // FEN initial position
"solution": ["c6h6", "g5h6", "c5e6", "h8h7", "e6g5",
"h7h8", "f7f8", "h6h7", "g5f7"], // solution moves
"rating": 1799 // puzzle glicko2 rating
}
```

Credits
-------

Expand Down
29 changes: 18 additions & 11 deletions app/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@ final class Env(
lazy val bus = lila.common.Bus(system)

lazy val preloader = new mashup.Preload(
lobby = Env.lobby.lobby,
lobbyVersion = () => Env.lobby.history.version,
featured = Env.tv.featured,
leaderboard = Env.user.cached.topToday.apply,
tv = Env.tv.tv,
leaderboard = Env.user.cached.topToday,
tourneyWinners = Env.tournament.winners.scheduled,
timelineEntries = Env.timeline.getter.userEntries _,
nowPlaying = Env.round.nowPlaying,
timelineEntries = Env.timeline.entryRepo.userEntries _,
dailyPuzzle = Env.puzzle.daily,
streamsOnAir = () => Env.tv.streamsOnAir,
countRounds = Env.round.count)
countRounds = Env.round.count,
lobbyApi = Env.api.lobbyApi,
getPlayban = Env.playban.api.currentBan _)

lazy val userInfo = mashup.UserInfo(
countUsers = () => Env.user.countEnabled,
bookmarkApi = Env.bookmark.api,
relationApi = Env.relation.api,
trophyApi = Env.user.trophyApi,
gameCached = Env.game.cached,
crosstableApi = Env.game.crosstableApi,
postApi = Env.forum.postApi,
getRatingChart = Env.history.ratingChartApi.apply,
getRanks = Env.user.cached.ranking.getAll,
getDonated = Env.donation.api.donatedByUser) _
isDonor = Env.donation.isDonor,
isHostingSimul = Env.simul.isHosting) _

system.actorOf(Props(new actor.Renderer), name = RendererName)

Expand Down Expand Up @@ -67,12 +68,14 @@ final class Env(
Env.notification,
Env.bookmark,
Env.pref,
Env.evaluation,
Env.chat,
Env.puzzle,
Env.tv,
Env.blog,
Env.relay)
Env.video,
Env.shutup, // required to load the actor
Env.relay
)
loginfo("[boot] Preloading complete")
}

Expand Down Expand Up @@ -119,10 +122,10 @@ object Env {
def setup = lila.setup.Env.current
def importer = lila.importer.Env.current
def tournament = lila.tournament.Env.current
def simul = lila.simul.Env.current
def relation = lila.relation.Env.current
def report = lila.report.Env.current
def pref = lila.pref.Env.current
def evaluation = lila.evaluation.Env.current
def chat = lila.chat.Env.current
def puzzle = lila.puzzle.Env.current
def coordinate = lila.coordinate.Env.current
Expand All @@ -132,5 +135,9 @@ object Env {
def qa = lila.qa.Env.current
def history = lila.history.Env.current
def worldMap = lila.worldMap.Env.current
def opening = lila.opening.Env.current
def video = lila.video.Env.current
def playban = lila.playban.Env.current
def shutup = lila.shutup.Env.current
def relay = lila.relay.Env.current
}
17 changes: 9 additions & 8 deletions app/Global.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lila.app

import lila.common.HTTPRequest
import play.api.mvc._
import play.api.mvc.Results._
import play.api.{ Application, GlobalSettings, Mode }
Expand All @@ -24,20 +25,20 @@ object Global extends GlobalSettings {
Env.i18n.requestHandler(req) orElse super.onRouteRequest(req)
}

private def niceError(req: RequestHeader): Boolean = req.method == "GET" && {
Env.ai.ServerOnly || (lila.common.HTTPRequest isSynchronousHttp req)
}
private def niceError(req: RequestHeader): Boolean =
req.method == "GET" &&
!Env.ai.ServerOnly &&
HTTPRequest.isSynchronousHttp(req) &&
!HTTPRequest.hasFileExtension(req)

override def onHandlerNotFound(req: RequestHeader) =
if (niceError(req)) controllers.Lobby.handleStatus(req, Results.NotFound)
else fuccess(NotFound)
else fuccess(NotFound("404 - Resource not found"))

override def onBadRequest(req: RequestHeader, error: String) =
if (error startsWith "Illegal character in path") fuccess(Redirect("/"))
else if (niceError(req)) {
logwarn("[global] bad request: " + error)
controllers.Lobby.handleStatus(req, Results.BadRequest)
}
else if (error startsWith "Cannot parse parameter") onHandlerNotFound(req)
else if (niceError(req)) controllers.Lobby.handleStatus(req, Results.BadRequest)
else fuccess(BadRequest(error))

override def onError(req: RequestHeader, ex: Throwable) =
Expand Down
5 changes: 4 additions & 1 deletion app/actor/Renderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private[app] final class Renderer extends Actor {
case lila.notification.actorApi.RenderNotification(id, from, body) =>
sender ! V.notification.view(id, from)(Html(body))

case lila.tournament.actorApi.RemindTournament(tournament) =>
case lila.tournament.actorApi.RemindTournament(tournament, _) =>
sender ! spaceless(V.tournament.reminder(tournament))

case lila.hub.actorApi.setup.RemindChallenge(gameId, from, _) =>
Expand All @@ -35,6 +35,9 @@ private[app] final class Renderer extends Actor {
case lila.tournament.actorApi.TournamentTable(tours) =>
sender ! spaceless(V.tournament.enterable(tours))

case lila.simul.actorApi.SimulTable(simuls) =>
sender ! spaceless(V.simul.allCreated(simuls))

case lila.puzzle.RenderDaily(puzzle, fen, lastMove) =>
sender ! spaceless(V.puzzle.daily(puzzle, fen, lastMove))

Expand Down
81 changes: 53 additions & 28 deletions app/controllers/Account.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,52 +30,66 @@ object Account extends LilaController {
}
}

def passwd = Auth { implicit ctx =>
me =>
Ok(html.account.passwd(me, forms.passwd)).fuccess
}

def info = Auth { implicit ctx =>
me =>
negotiate(
html = notFound,
api = _ => Env.round.nowPlaying(me, 5) map { nowPlaying =>
api = _ => lila.game.GameRepo urgentGames me map { povs =>
Ok {
import play.api.libs.json._
Env.user.jsonView(me, extended = true) ++ Json.obj(
"nowPlaying" -> JsArray(nowPlaying map { pov =>
Json.obj(
"id" -> pov.fullId,
"variant" -> pov.game.variant.key,
"speed" -> pov.game.speed.key,
"perf" -> lila.game.PerfPicker.key(pov.game),
"rated" -> pov.game.rated,
"opponent" -> Json.obj(
"id" -> pov.opponent.userId,
"username" -> lila.game.Namer.playerString(pov.opponent, withRating = false)(Env.user.lightUser),
"rating" -> pov.opponent.rating
)
)
})
)
"nowPlaying" -> JsArray(povs take 9 map Env.api.lobbyApi.nowPlaying))
}
}
)
}

def passwd = Auth { implicit ctx =>
me =>
Ok(html.account.passwd(me, forms.passwd)).fuccess
}

def passwdApply = AuthBody { implicit ctx =>
me =>
implicit val req = ctx.body
FormFuResult(forms.passwd) { err =>
fuccess(html.account.passwd(me, err))
} { passwd =>
} { data =>
for {
ok UserRepo.checkPassword(me.id, passwd.oldPasswd)
_ ok ?? UserRepo.passwd(me.id, passwd.newPasswd1)
} yield ok.fold(
Redirect(routes.User show me.username),
BadRequest(html.account.passwd(me, forms.passwd))
)
ok UserRepo.checkPassword(me.id, data.oldPasswd)
_ ok ?? UserRepo.passwd(me.id, data.newPasswd1)
} yield {
val content = html.account.passwd(me, forms.passwd.fill(data), ok.some)
ok.fold(Ok(content), BadRequest(content))
}
}
}

private def emailForm(id: String) = UserRepo email id map { email =>
forms.email.fill(forms.Email(~email, ""))
}

def email = Auth { implicit ctx =>
me =>
emailForm(me.id) map { form =>
Ok(html.account.email(me, form))
}
}

def emailApply = AuthBody { implicit ctx =>
me =>
implicit val req = ctx.body
FormFuResult(forms.email) { err =>
fuccess(html.account.email(me, err))
} { data =>
for {
ok UserRepo.checkPassword(me.id, data.passwd)
_ ok ?? UserRepo.email(me.id, data.email)
form <- emailForm(me.id)
} yield {
val content = html.account.email(me, form, ok.some)
ok.fold(Ok(content), BadRequest(content))
}
}
}

Expand All @@ -93,4 +107,15 @@ object Account extends LilaController {
Redirect(routes.User show me.username) withCookies LilaCookie.newSession
}
}

def kid = Auth { implicit ctx =>
me =>
Ok(html.account.kid(me)).fuccess
}

def kidConfirm = Auth { ctx =>
me =>
implicit val req = ctx.req
(UserRepo toggleKid me) inject Redirect(routes.Account.kid)
}
}
7 changes: 5 additions & 2 deletions app/controllers/Ai.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import play.api.mvc._

import lila.ai.actorApi.{ Chess960, KingOfTheHill, Variant }
import lila.api.Context
import lila.app._

Expand All @@ -10,12 +11,14 @@ import play.api.Play.current

object Ai extends LilaController {

private def requestVariant(req: RequestHeader): Variant = Variant(~get("variant", req))

def move = Action.async { req =>
Env.ai.server.move(
uciMoves = get("uciMoves", req) ?? (_.split(' ').toList),
initialFen = get("initialFen", req),
level = getInt("level", req) | 1,
kingOfTheHill = getBool("kingOfTheHill", req)
variant = requestVariant(req)
) fold (
err => {
logwarn("[ai] stockfish server play: " + err)
Expand All @@ -31,7 +34,7 @@ object Ai extends LilaController {
uciMoves = get("uciMoves", req) ?? (_.split(' ').toList),
initialFen = get("initialFen", req),
requestedByHuman = getBool("human", req),
kingOfTheHill = getBool("kingOfTheHill", req)
variant = requestVariant(req)
).effectFold(
err => WS.url(replyToUrl).post(err.toString),
infos => WS.url(replyToUrl).post(lila.analyse.Info encodeList infos)
Expand Down
Loading

0 comments on commit f9e37fb

Please sign in to comment.