рд╣рдо рдХреИрдирд╡рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдВрдбреНрд░реЙрдЗрдб рдХреЗ рд▓рд┐рдП рдПрдХ рдЧреЗрдо рд▓рд┐рдЦ рд░рд╣реЗ рд╣реИрдВ

рд╣рд╛рдп рд╣рдорд░!
рдЖрдЬ рдореИрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдХреИрдирд╡рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдВрдбреНрд░реЙрдЗрдб рдУрдПрд╕ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рд░рд▓ рд▓реЙрдЬрд┐рдХ рдЧреЗрдо рдХреИрд╕реЗ рд▓рд┐рдЦрд╛ рдЬрд╛рдПред рдореИрдВ рдЗрд╕ рдЦреЗрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ 5 рд╕рд╛рд▓ рдкрд╣рд▓реЗ рдЕрдкрдиреЗ рдлреЛрди рдкрд░ рдорд┐рд▓рд╛ рдерд╛ред рдирд╛рдо рднреВрд▓ рдЧрдпрд╛ рдерд╛, рдФрд░ рдХрдИ рд╡рд┐рд╖рдпрдЧрдд рдордВрдЪреЛрдВ рдкрд░ рдЦреЛрдЬ рдХрд░рдиреЗ рд╕реЗ рдХреБрдЫ рднреА рдирд╣реАрдВ рд╣реБрдЖ, рдФрд░ рдореИрдВрдиреЗ рдЗрд╕ рдЦреЗрд▓ рдХреЗ рдЕрдкрдиреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред рдореЗрд░реЗ рд▓рд┐рдП рд╡рд┐рдХрд╛рд╕ рдЕрдзрд┐рдХ рд╢реМрдХ рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдХрднреА-рдХрднреА рдореИрдВ рдЫреЛрдЯреА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдкрд░ рдХрд╛рдо рдХрд░рддрд╛ рд╣реВрдВред рдХреБрдЫ рд╡рд┐рдЪрд╛рд░ рдХреЗ рдмрд╛рдж, рдореИрдВрдиреЗ рдЗрдВрдЬрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди рдЕрдкрдиреЗ рджрдо рдкрд░ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдПред рдЗрд╕ рдирд┐рд░реНрдгрдп рдХреЗ рд▓рд┐рдП рдХрд╛рд░рдг: рдХреИрдирд╡рд╕ рдХреЗ рд╕рд╛рде рдЕрдиреБрднрд╡ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЗрдЪреНрдЫрд╛ред

рд▓рдмреНрдмреЛрд▓реБрдЖрдм рдпрд╣ рд╣реИ рдХрд┐ ...



рдПрдХ рд╢рддрд░рдВрдЬ рдХреЗ рд╕рдорд╛рди рдЦреЗрд▓ рдХрд╛ рдореИрджрд╛рди рд╣реИ, рд▓реЗрдХрд┐рди рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдХреЛ рдХрд╛рд▓реЗ рдФрд░ рд╕рдлреЗрдж рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд┐рдП рдмрд┐рдирд╛ред рдХреНрд╖реЗрддреНрд░ рдХрд╛ рдЖрдХрд╛рд░ рдордирдорд╛рдирд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди 2x3 рд╕реЗ рдЫреЛрдЯреЗ рдХреНрд╖реЗрддреНрд░ рдкрд░ рдЦреЗрд▓рдирд╛ рдмрд╣реБрдд рдорд╛рдпрдиреЗ рдирд╣реАрдВ рд░рдЦрддрд╛ рд╣реИред рдореБрдЭреЗ 10x10 рдХреЗ рдореИрджрд╛рди рдкрд░ рдЦреЗрд▓рдирд╛ рдкрд╕рдВрдж рд╣реИред

рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдордирдорд╛рдиреА рд╣реЛ рд╕рдХрддреА рд╣реИ, рд▓реЗрдХрд┐рди рдореЗрд░реА рд░рд╛рдп рдореЗрдВ рдПрдХ рд╕рд╛рде рдпрд╛ рдЪрд╛рд░ рдЦреЗрд▓рдирд╛ рдЕрдзрд┐рдХ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИред рдпрджрд┐ рдЖрдк рдПрдХ рдЖрдпрддрд╛рдХрд╛рд░ рдореИрджрд╛рди рдкрд░ рдПрдХ рд╕рд╛рде рддреАрди рдЦреЗрд▓рддреЗ рд╣реИрдВ, рддреЛ рдПрдХ рдЕрд╕рдВрддреБрд▓рди рд╣реЛрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ "рдЦрд╛рд▓реА" рдХреЛрдиреЗ рд╕реЗ рджреВрд░ рд╣реЛрдЧрд╛ред рдпрджрд┐ рдЖрдк 4 рд╕реЗ рдЕрдзрд┐рдХ рд▓реЛрдЧреЛрдВ рдХреЗ рд╕рд╛рде рдЦреЗрд▓рддреЗ рд╣реИрдВ, рддреЛ рдЕрдкрдиреА рд░рдгрдиреАрддрд┐ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИред

рдкреНрд░рддреНрдпреЗрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЛ рдмрджрд▓реЗ рдореЗрдВ рдПрдХ рд╡рд╕реНрддреБ (рдЪрд▓реЛ рдЗрд╕реЗ рдПрдХ рдкрд░рдорд╛рдгреБ рдХрд╣рддреЗ рд╣реИрдВ) рдПрдХ рдирд┐: рд╢реБрд▓реНрдХ рд╕реЗрд▓ рдпрд╛ рдПрдХ рд╕реЗрд▓ рдореЗрдВ рд░рдЦрдиреА рдЪрд╛рд╣рд┐рдП рдЬрд╣рд╛рдВ рдЗрд╕рдХреЗ рдкрд░рдорд╛рдгреБ рдкрд╣рд▓реЗ рд╕реЗ рдореМрдЬреВрдж рд╣реИрдВред рдпрджрд┐ рдПрдХ "рдорд╣рддреНрд╡рдкреВрд░реНрдг рджреНрд░рд╡реНрдпрдорд╛рди" рдПрдХ рд╕реЗрд▓ рдореЗрдВ рдЬрдорд╛ рд╣реЛрддрд╛ рд╣реИ, рддреЛ рдкрдбрд╝реЛрд╕реА рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЗ рдмрд░рд╛рдмрд░, рддреЛ рдЗрд╕ рд╕реЗрд▓ рд╕реЗ рдкрд░рдорд╛рдгреБ рдкрдбрд╝реЛрд╕реА рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдореЗрдВ рдЪрд▓реЗ рдЬрд╛рддреЗ рд╣реИрдВ, рдЬрдмрдХрд┐ рдкрдбрд╝реЛрд╕реА рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдореЗрдВ рдкрд░рдорд╛рдгреБрдУрдВ рдХреЛ "рдХреИрдкреНрдЪрд░" рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рддреНред рд╡реЗ рдЕрдм рдЙрд╕ рдЦрд┐рд▓рд╛рдбрд╝реА рдХреЗ рд╣реИрдВ рдЬрд┐рдирдХреЗ рдкрд░рдорд╛рдгреБ рдмрд┐рдЦрд░реЗ рд╣реБрдП рд╣реИрдВред

рдмрд┐рдВрджреБ рдХреЛ рд╕реНрдкрд╖реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЪрд┐рддреНрд░реЛрдВ рдХреА рдПрдХ рдЬреЛрдбрд╝реАред



рдПрдХ рдЦрд╛рд▓реА 4x4 рдХреНрд╖реЗрддреНрд░ рдкреНрд░рддреНрдпреЗрдХ рдХреЛрд╢рд┐рдХрд╛ рдХреЗ рд▓рд┐рдП рдорд╣рддреНрд╡рдкреВрд░реНрдг рджреНрд░рд╡реНрдпрдорд╛рди рдХрд╛ рд╕рдВрдХреЗрдд рджреЗрддрд╛ рд╣реИред


рддреАрд╕рд░реА рдЪрд╛рд▓ рдХреЗ рдмрд╛рдж рдЦреЗрд▓ рдХреА рд╕реНрдерд┐рддрд┐ред


рдЪреМрдереА рдЪрд╛рд▓ рдХреЗ рдмрд╛рдж рдЦреЗрд▓ рдХреА рд╕реНрдерд┐рддрд┐ (рдкрд╣рд▓рд╛ рдиреАрд▓рд╛)ред рдпрд╣ рджреЗрдЦрд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдКрдкрд░реА рдмрд╛рдПрдВ рдХреЛрдиреЗ рдореЗрдВ рдкрд░рдорд╛рдгреБрдУрдВ рдХреА рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╕рдВрдЦреНрдпрд╛ рдЬрдорд╛ рд╣реЛ рдЧрдИ рд╣реИред


рдмрд╛рд╣! рдЙрдиреНрд╣реЛрдВрдиреЗ рдмрд┐рдЦрд░реЗ рд╣реБрдП, рд╕реЗрд▓ рдореЗрдВ 2 рдиреАрд▓реЗ рдкрд░рдорд╛рдгреБрдУрдВ рдХреЛ рдХреИрдкреНрдЪрд░ рдХрд┐рдпрд╛ [0] [1]ред рдФрд░ рдЗрд╕ рд╕реЗрд▓ рдореЗрдВ [0] [1] рдЕрдм рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдорд╛рддреНрд░рд╛ рднреА рд╣реИред рдЪреЗрди рд░рд┐рдПрдХреНрд╢рди!


рд╡рд┐рд╕реНрддрд╛рд░ рдХреЗ рдмрд╛рдж рдХреА рд╕реНрдерд┐рддрд┐ред рдЪреМрдереА рдЪрд╛рд▓ рдХрд╛ рдЕрдВрддред рдиреАрд▓рд╛ рдЕрдм рдкрд╛рдБрдЪрд╡рд╛рдБ рдореВрд╡ рдХрд░реЗрдЧреАред


рдмреЛрдзред рдЧреНрд░рд╛рдлрд┐рдХ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИред



рдЖрдЗрдП рдХреНрд░рд┐рдпрд╛рдиреНрд╡рдпрди рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рдЙрддрд░реЗрдВред рдЖрдЗрдП View рд╕реЗ рд╡реНрдпреБрддреНрдкрдиреНрди рдПрдХ рдХреНрд▓рд╛рд╕ рдмрдирд╛рдПрдВред
public class GameView extends View { private Bitmap mBitmap; private Canvas mCanvas; private Paint paint, mBitmapPaint; private float canvasSize; private final int horizontalCountOfCells, verticalCountOfCells; public GameView(Context context, AttributeSet attrs) { super(context, attrs); //   horizontalCountOfCells =10; verticalCountOfCells =10; // xml       300dp canvasSize=(int)convertDpToPixel(300, context); mBitmap = Bitmap.createBitmap((int) canvasSize, (int) canvasSize, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mBitmapPaint = new Paint(Paint.DITHER_FLAG); //  ,       paint =new Paint(); paint.setAntiAlias(true); paint.setDither(true); paint.setColor(0xffff0505); paint.setStrokeWidth(5f); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); //  for(int x=0;x< horizontalCountOfCells +1;x++) mCanvas.drawLine((float)x* canvasSize / horizontalCountOfCells, 0, (float)x* canvasSize / horizontalCountOfCells, canvasSize, paint); for(int y=0;y< verticalCountOfCells +1;y++) mCanvas.drawLine(0, (float)y* canvasSize / verticalCountOfCells, canvasSize, (float)y* canvasSize / verticalCountOfCells, paint); } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); } // dp   public float convertDpToPixel(float dp,Context context){ Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return dp * (metrics.densityDpi/160f); } } 


рдпрд╣рд╛рдБ рдореИрдВрдиреЗ рдЦрд░рд╛рдм рдЧреБрдгрд╡рддреНрддрд╛ рд╡рд╛рд▓реЗ рдХреЛрдб рдХреЗ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рдЭрдЯрдХреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреА рд╣реИ , рдХреЛрдб рдореЗрдВ рд╣рд╛рд░реНрдб-рдХреЛрдбрд┐рдд рд╣реИ рдЬреЛ рдХрд┐ рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХреА рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдХреЛ рдЕрд▓рдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реА рд▓рд╛рдЗрдиреЛрдВ рдХрд╛ рд░рдВрдЧ рдФрд░ рджреГрд╢реНрдп рдХрд╛ рдЖрдХрд╛рд░ 300 рдбреАрдкреА рдХреЗ рдмрд░рд╛рдмрд░ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИред рдпрд╣ рдЖрдХрд╛рд░ рдПрдЯреНрд░реАрдмреНрдпреВрдЯрд╕реЗрдЯ рдХреНрд▓рд╛рд╕ рдХреЗ рдПрдЯрд░рд╕ рдСрдмреНрдЬреЗрдХреНрдЯ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдо рдХреЛрдб рдХреЛ рдЕрд╡реНрдпрд╡рд╕реНрдерд┐рдд рдирд╣реАрдВ рдХрд░реЗрдВрдЧреЗред

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдо рддреБрд░рдВрдд рд╕реНрдХреЗрдЪ рдЧрддрд┐рд╡рд┐рдзрд┐ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рд╕рдм рдХреБрдЫ рдЦреВрдмрд╕реВрд░рддреА рд╕реЗ рдЦреАрдВрдЪрд╛ рдЧрдпрд╛ рд╣реИред

 public class GameActivity extends Activity { Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } 


рдФрд░ рдорд╛рд░реНрдХрдЕрдк main.xml

 <LinearLayout xmlns:android=┬лschemas.android.com/apk/res/android┬╗ android:orientation=┬лvertical┬╗ android:layout_width=┬лfill_parent┬╗ android:layout_height=┬лfill_parent┬╗ android:background="#aaa" android:gravity=┬лcenter_horizontal┬╗> <com.devindi.chain.GameView android:layout_width=┬л300dp┬╗ android:layout_height=┬л300dp┬╗ android:id="@+id/game_view" android:layout_gravity=┬лcenter┬╗ android:background="#000"/> </LinearLayout> 


рдЗрд╕ рд╕реНрдЯреЗрдк рдХреЗ рдмрд╛рдж рдХреЛрдбред

рдЕрдм рдЪрд▓реЛ рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХреЗ рдкреИрдорд╛рдиреЗ рдХреЛ рдмрджрд▓рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХреЗ рдЫреЛрдЯреЗ рдЖрдХрд╛рд░ рдХреЗ рдХрд╛рд░рдг рд╡рд╛рдВрдЫрд┐рдд рд╕реЗрд▓ рд╕реЗ рдЕрддреАрдд рдХреЛ рдпрд╛рдж рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдЕрдкрдиреЗ GameView рд╡рд░реНрдЧ рдореЗрдВ OnScaleGestureListener рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ ScaleGestureDetector.SimpleOnScaleGestureListener рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЛрдВ рдХреЛ рдлрд┐рд░ рд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред

  private final ScaleGestureDetector scaleGestureDetector; private final int viewSize; private float mScaleFactor; public GameView(Context context, AttributeSet attrs) { // xml       300dp viewSize=(int)convertDpToPixel(300, context); mScaleFactor=1f;//    canvasSize=(int)(viewSize*mScaleFactor);//   тАж scaleGestureDetector=new ScaleGestureDetector(context, new MyScaleGestureListener()); } @Override protected void onDraw(Canvas canvas) { canvas.save(); canvas.scale(mScaleFactor, mScaleFactor);//  canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); canvas.restore(); } //      MyScaleGestureListener @Override public boolean onTouchEvent(MotionEvent event) { scaleGestureDetector.onTouchEvent(event); return true; } //  ScaleGestureDetector.SimpleOnScaleGestureListener,       //  OnScaleGestureListener private class MyScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { // ""  @Override public boolean onScale(ScaleGestureDetector scaleGestureDetector) { float scaleFactor=scaleGestureDetector.getScaleFactor();//      //    -    float focusX=scaleGestureDetector.getFocusX(); float focusY=scaleGestureDetector.getFocusY(); //               2  if(mScaleFactor*scaleFactor>1 && mScaleFactor*scaleFactor<2){ mScaleFactor *= scaleGestureDetector.getScaleFactor(); canvasSize =viewSize*mScaleFactor;//       //   //         . //  ,     // ,      //        ( ). int scrollX=(int)((getScrollX()+focusX)*scaleFactor-focusX); scrollX=Math.min( Math.max(scrollX, 0), (int) canvasSize -viewSize); int scrollY=(int)((getScrollY()+focusY)*scaleFactor-focusY); scrollY=Math.min( Math.max(scrollY, 0), (int) canvasSize -viewSize); scrollTo(scrollX, scrollY); } //   invalidate(); return true; } } 


рдкрд░рд┐рдгрд╛рдо рдХреЛрдб

рдХреГрдкрдпрд╛ рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдЖрд╡рд░реНрдзрди рдореВрд▓реНрдп рдХреА рд╕реАрдорд╛рдПрдВ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХреА рдЬрд╛рддреА рд╣реИрдВ (рд╡реГрджреНрдзрд┐ 1 рд╕реЗ 2 рддрдХ рд╣реЛрдЧреА), рдФрд░ рдЦреЗрд▓ рдореИрджрд╛рди рдХреЛ рдЦреЗрд▓ рдореИрджрд╛рди рдХреЗ рдмрд╛рд╣рд░ рдХреНрд╖реЗрддреНрд░ рдХреЛ рджрд┐рдЦрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЪреЗрдХ рдХреЗ рд╕рд╛рде рдЬрд╝реВрдо рдмрд┐рдВрджреБ (рдЙрдВрдЧрд▓рд┐рдпреЛрдВ рдХреЗ рдмреАрдЪ рдмреАрдЪ рдореЗрдВ рдмрд┐рдВрджреБ) рдкрд░ рд╕реНрдХреНрд░реЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЬрд╝реВрдо рдкреЙрдЗрдВрдЯ (рдлреЛрдХрд▓ рдкреЙрдЗрдВрдЯ) рдкрд░ рд╕реНрдХреНрд░реЙрд▓ рдирд┐рдореНрди рдкреНрд░рдХрд╛рд░ рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рдХреИрдирд╡рд╛рд╕ рдХреА рд╢реБрд░реБрдЖрдд (рдКрдкрд░реА рдмрд╛рдПрдВ рдХреЛрдиреЗ) рдХреЗ рд╕рд╛рдкреЗрдХреНрд╖ рдлреЛрдХрд▓ рдкреЙрдЗрдВрдЯ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреА рдЧрдгрдирд╛ рдХреА рдЬрд╛рддреА рд╣реИ, рдЬрд╝реВрдо рдХрд╛рд░рдХ рд╕реЗ рдЧреБрдгрд╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рджреГрд╢реНрдп рдХреЗ рд╕рд╛рдкреЗрдХреНрд╖ рдмрд┐рдВрджреБ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдШрдЯрд╛рдП рдЬрд╛рддреЗ рд╣реИрдВред рдЙрд╕рдХреЗ рдмрд╛рдж, рд╣рдо рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХреЗ рдмрд╛рд╣рд░ рд╕реНрдХреНрд░реЙрд▓рд┐рдВрдЧ рдХреЛ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдВрддрд░рд╛рд▓ [0, рдХреИрдирд╡рд╕рд╛рдЗрдЬ -рд╡реНрдпреВрд╕рд╛рдЗрдЬрд╝] рд╕реЗ рдирд┐рдХрдЯрддрдо рдореВрд▓реНрдп рд▓реЗрддреЗ рд╣реИрдВред

рдЕрдм рд╣рдо рд╕реНрдХреНрд░реЙрд▓, рд╕рд┐рдВрдЧрд▓ рдЯреИрдк рдФрд░ рдбрдмрд▓ рдЯреИрдк рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рд▓рд┐рдЦреЗрдВрдЧреЗ (рдбрдмрд▓ рдЯреИрдк рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХреЗ рдореВрд▓ рдкреИрдорд╛рдиреЗ рдкрд░ рд▓реМрдЯ рдЖрдПрдЧрд╛)ред

  private final GestureDetector detector; public GameView(Context context, AttributeSet attrs) { ... detector=new GestureDetector(context, new MyGestureListener()); } //      Motion Event' MyGestureListener'  MyScaleGestureListener' @Override public boolean onTouchEvent(MotionEvent event) { detector.onTouchEvent(event); scaleGestureDetector.onTouchEvent(event); return true; } //  GestureDetector.SimpleOnGestureListener,     //    OnGestureListener private class MyGestureListener extends GestureDetector.SimpleOnGestureListener { //  (   ) @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //       if(getScrollX()+distanceX< canvasSize -viewSize && getScrollX()+distanceX>0){ scrollBy((int)distanceX, 0); } //       if(getScrollY()+distanceY< canvasSize -viewSize && getScrollY()+distanceY>0){ scrollBy(0, (int)distanceY); } return true; } //   @Override public boolean onSingleTapConfirmed(MotionEvent event){ //  ,    int cellX=(int)((event.getX()+getScrollX())/mScaleFactor); int cellY=(int)((event.getY()+getScrollY())/mScaleFactor); return true; } //   @Override public boolean onDoubleTapEvent(MotionEvent event){ //     mScaleFactor=1f; canvasSize =viewSize; scrollTo(0, 0);//,      . invalidate();//  return true; } } 


рдкрд░рд┐рдгрд╛рдо рдХреЛрдб
рдПрдХ рдЯреИрдк рд╕реЗ, рд╣рдо рдЙрд╕ рд╕реЗрд▓ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреА рдЧрдгрдирд╛ рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕ рдкрд░ рд╣рдо рдЯреИрдк рдХрд░рддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдКрдкрд░реА рдмрд╛рдПрдВ рд╕реЗрд▓ рдореЗрдВ 0,0 рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╣реЛрдВрдЧреЗред

рдЪрд▓реЛ рдкрд░рдорд╛рдгреБ рдбреНрд░рд╛рдЗрдВрдЧ рд╡рд┐рдзрд┐ drawAtoms рд▓рд┐рдЦреЗрдВред рд╡рд┐рдзрд┐ рдкреИрд░рд╛рдореАрдЯрд░ - рд╕реЗрд▓ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдкрд░рдорд╛рдгреБрдУрдВ рдХрд╛ рд░рдВрдЧ, рд░рдВрдЧ рдФрд░ рд╕рдВрдЦреНрдпрд╛ рдЖрдХрд░реНрд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред

  void drawAtoms(int cellX, int cellY, int color, int count){ //    float x0=((1f/(2* horizontalCountOfCells))*viewSize+(1f/ horizontalCountOfCells)*cellX*viewSize); float y0=((1f/(2* verticalCountOfCells))*viewSize+(1f/ verticalCountOfCells)*cellY*viewSize); paint.setColor(color); switch (count){ //todo non-absolute values case 1: drawAtoms(cellX, cellY, color, 0);//   mCanvas.drawCircle(x0, y0, 3, paint);//      break; case 2: drawAtoms(cellX, cellY, color, 0); //        mCanvas.drawCircle(x0-7, y0, 3, paint); mCanvas.drawCircle(x0+7, y0, 3, paint); break; case 3: drawAtoms(cellX, cellY, color, 0); //            mCanvas.drawCircle(x0 - 7, y0 + 4, 3, paint); mCanvas.drawCircle(x0 + 7, y0 + 4, 3, paint); mCanvas.drawCircle(x0, y0-8, 3, paint); break; case 4: drawAtoms(cellX, cellY, color, 0); // 4          mCanvas.drawCircle(x0-7, y0-7, 3, paint); mCanvas.drawCircle(x0-7, y0+7, 3, paint); mCanvas.drawCircle(x0+7, y0+7, 3, paint); mCanvas.drawCircle(x0+7, y0-7, 3, paint); break; case 0: //    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); //   paint.setStyle(Paint.Style.FILL); //  ,       mCanvas.drawCircle(x0, y0, 17, paint); //   paint.setStyle(Paint.Style.STROKE); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); break; } invalidate();//  } 


рдлрд┐рд▓рд╣рд╛рд▓, рд╡рд┐рдзрд┐ рдореЗрдВ рдкрд░рдорд╛рдгреБрдУрдВ рдХреЗ рдЖрдХрд╛рд░ рдФрд░ рдЙрдирдХреЗ рдмреАрдЪ рдХреА рджреВрд░реА рдХреЗ рдкреВрд░реНрдг рдореВрд▓реНрдпреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рджреЛрд╖ рд╣реИред рдЗрд╕рд╕реЗ рдХреНрдпрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ? рдпрджрд┐ рдлрд╝реАрд▓реНрдб рдХрд╛ рдЖрдХрд╛рд░ 10x10 рд╕реЗ рдХрдо рд╣реИ, рддреЛ рдкрд░рдорд╛рдгреБ рдЫреЛрдЯреЗ рджрд┐рдЦрд╛рдИ рджреЗрдВрдЧреЗ, рдФрд░ рдПрдХ рдмрдбрд╝реЗ рдХреНрд╖реЗрддреНрд░ рдЖрдХрд╛рд░ рдХреЗ рд╕рд╛рде рд╡реЗ рд╕реЗрд▓ рдореЗрдВ рдлрд┐рдЯ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред

рдпрд╣ рд╕реНрдХреНрд░реЙрд▓рдмрд╛рд░ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИред рдпрд╣ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдпрд╣рд╛рдБ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рд╡рд░реНрдгрд┐рдд рд╣реИ habrahabr.ru/post/120931 ред

рдЧреЗрдо рд▓реЙрдЬрд┐рдХ рдХреЗ рд╡рд┐рд╡рд░рдг рдХреЗ рд╕рд╛рде рдЖрдЧреЗ рдмрдврд╝рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдЧреЗрдо рд▓реЙрдЬрд┐рдХ рдкреНрд░рдХрд╛рд░ (рддрд░реНрдХ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рд╣рдорд╛рд░рд╛ рд╡рд░реНрдЧ) рдирд╛рдо рд▓реЙрдЬрд┐рдХ рдХреЗ рд╕рд╛рде рдПрдХ рд╡реИрд╢реНрд╡рд┐рдХ рдЪрд░ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдПрдХ рдПрдХрд▓ рдЯреИрдк рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рд╡рд┐рдзрд┐ рдореЗрдВ onSingleTapConfirmed рдЬреЛрдбрд╝реЗрдВ
рдирдП рдкрд░рдорд╛рдгреБ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреА рддрд╛рд░реНрдХрд┐рдХ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рднрд╡рд┐рд╖реНрдп рдХреЗ рддрд░реАрдХреЗ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ред
logic.addAtom (cellX, cellY);
рдЗрд╕ рдЪрд░ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реЗрдЯрд░ рдХреА рднреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдХреЛрдбред

рдмреЛрдзред рдЦреЗрд▓ рддрд░реНрдХред



рдЧреЗрдо рдХреЗ рддрд░реНрдХ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдЧреЗрдорд▓рд╛рдЗрдЬрд┐рдХ рдХреНрд▓рд╛рд╕ рдмрдирд╛рдПрдВред рд╣рдореЗрдВ рдЗрдирд░ рдХреНрд▓рд╛рд╕ рд╕реЗрд▓ рдХреА рднреА рдЬрд░реВрд░рдд рд╣реИ, рдЬреЛ рдкреНрд▓реЗрдЗрдВрдЧ рд╕реЗрд▓ рдХреЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рд╕реНрдЯреЛрд░ рдХрд░рддреА рд╣реИред

  public class GameLogic { private class Cell{ int player=0, countOfAtoms=0;// ,     final int maxCountOfAtoms;//  Cell(int maxCountOfAtoms){ this.maxCountOfAtoms=maxCountOfAtoms; } public int getCountOfAtoms() { return countOfAtoms; } public int getPlayer() { return player; } public void setPlayer(int player) { this.player = player; } public void resetCount() { this.countOfAtoms = 0; } public void addAtom(){ this.countOfAtoms++; } boolean isFilled(){ return this.countOfAtoms == this.maxCountOfAtoms; } } } 


GameLogic рд╡рд░реНрдЧ рд╣реА

  private final GameView view; private final GameActivity activity; private int moveNumber=0, currentPlayer=0; private final int COUNT_OF_PLAYERS, BOARD_WIDTH, BOARD_HEIGHT; private final Cell[][] cells; private final int[] colors={0xff1d76fc, 0xfffb1d76, 0xff76fb1d, 0xffa21cfb};//  private final Handler mHandler; public GameLogic(GameView view, GameActivity activity) { this.view = view; this.activity=activity; mHandler=new Handler(); //   ( ,  ) this.COUNT_OF_PLAYERS=2; this.BOARD_HEIGHT=10; this.BOARD_WIDTH=10; cells=new Cell[BOARD_WIDTH][BOARD_HEIGHT]; for(int x=0; x<BOARD_WIDTH; x++){ for(int y=0; y<BOARD_HEIGHT; y++){ if((x==0 || x==BOARD_WIDTH-1) && (y==0 || y==BOARD_HEIGHT-1)){ cells[x][y]=new Cell(2);//    2 }else if((x==0 || x==BOARD_WIDTH-1) || (y==0 || y==BOARD_HEIGHT-1)){ cells[x][y]=new Cell(3);//,    - 3 }else{ cells[x][y]=new Cell(4);// - 4 } } } } //      public void addAtom(final int cellX, final int cellY) { // ,    ,      -   . final Cell currentCell; try{ currentCell=cells[cellX][cellY]; }catch (IndexOutOfBoundsException ex){ return; } //        if(currentCell.getPlayer()==currentPlayer){ currentCell.addAtom(); view.drawAtoms(cellX, cellY, colors[currentPlayer], currentCell.getCountOfAtoms()); //   if(currentCell.isFilled()){ final List<Cell> nearby=new ArrayList<Cell>(4);//   selfAddCell(cellX, cellY-1, nearby); selfAddCell(cellX, cellY+1, nearby); selfAddCell(cellX-1, cellY, nearby); selfAddCell(cellX+1, cellY, nearby); for(Cell nearbyCell:nearby){ nearbyCell.setPlayer(currentPlayer);//     } delayedAddAtom(cellX, cellY-1); delayedAddAtom(cellX, cellY+1); delayedAddAtom(cellX-1, cellY); delayedAddAtom(cellX+1, cellY); //     run() mHandler.postDelayed(new Runnable() { @Override public void run() { //    () currentCell.setPlayer(-1); //  currentCell.resetCount(); view.drawAtoms(cellX, cellY, 0x000000, 0); } }, 1000); return; } }else if(currentCell.getPlayer()==-1){ currentCell.addAtom(); view.drawAtoms(cellX, cellY, colors[currentPlayer], currentCell.getCountOfAtoms()); currentCell.setPlayer(currentPlayer); }else{ return; } } //       private void delayedAddAtom(final int cellX, final int cellY){ mHandler.postDelayed(new Runnable() { @Override public void run() { addAtom(cellX, cellY); } }, 1000); } //   target   private void selfAddCell(int cellX, int cellY, List<Cell> target){ try{ target.add(cells[cellX][cellY]); }catch (IndexOutOfBoundsException ignore){} } 


рдХреЛрдбред

рдпрд╣рд╛рдБ рд╣рдо рдПрдХ рдирд┐рд░реНрдорд╛рдгрдХ рдХреЛ рджреЗрдЦрддреЗ рд╣реИрдВ рдЬреЛ рд╕реЗрд▓ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдХрд╛ рдПрдХ рджреЛ-рдЖрдпрд╛рдореА рд╕рд░рдгреА рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ addAtom рдкрджреНрдзрддрд┐, рдЬрд┐рд╕реЗ рдПрдХрд▓ рдЯреИрдк рджреГрд╢реНрдп рд╕реЗ рдФрд░ рдЦреБрдж рд╕реЗ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рдмрд╢рд░реНрддреЗ рдХрд┐ рд╕реЗрд▓ рдкрд░рдорд╛рдгреБрдУрдВ рд╕реЗ рдиреЗрддреНрд░рдЧреЛрд▓рдХ рддрдХ рднрд░ рдЬрд╛рддрд╛ рд╣реИред
рдЕрдм рдЖрдк рдкрд░рдорд╛рдгреБрдУрдВ рдХреЛ рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдореЗрдВ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдЬрдм рдХреЛрд╢рд┐рдХрд╛ рдореЗрдВ рдкрд░рдорд╛рдгреБрдУрдВ рдХреА рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╕рдВрдЦреНрдпрд╛ рдЬрдорд╛ рд╣реЛрддреА рд╣реИ, рддреЛ рд╡реЗ рдПрдХ рд╕реЗрдХрдВрдб рдореЗрдВ рдЕрд▓рдЧ рд╣реЛ рдЬрд╛рдПрдВрдЧреЗред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЕрдм рдЗрд╕ рджреВрд╕рд░реЗ рдХреЗ рджреМрд░рд╛рди рдЖрдк рдкрд░рдорд╛рдгреБ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕ рд╕реЗ рд╣рдЯрд╛рдПрдВ рдЖрдЗрд▓реЙрдХ рдлреНрд▓реИрдЧ рд╡реИрд░рд┐рдПрдмрд▓ рдФрд░ рдЖрдИрд▓реЙрдХ (), рд▓реЙрдХ (), рдФрд░ рдЕрдирд▓реЙрдХ () рдЧреЗрдорд╡реНрдпреВ рд╡рд░реНрдЧ рдХреЗ рддрд░реАрдХреЛрдВ рдХреЛ рдЬреЛрдбрд╝рдХрд░ред

рдЪрд╛рд▓ рдХреЗ рдмрд╛рдж рдПрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рдкрд░рд┐рд╡рд░реНрддрди рдЬреЛрдбрд╝рдирд╛ рднреА рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рдЦреЗрд▓ рдХреЗ рдЕрдВрдд рдореЗрдВ рд╕реНрдХреЛрд░рд┐рдВрдЧ рдФрд░ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг (рдЬрдм рд╕рднреА рдкрд░рдорд╛рдгреБ рдПрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реЛрддреЗ рд╣реИрдВ рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдЦрд┐рд▓рд╛рдбрд╝реА рдХрдо рд╕реЗ рдХрдо 1 рдЪрд╛рд▓ рдЪрд▓рддрд╛ рд╣реИ)ред
AddAtom () рд╡рд┐рдзрд┐ рдХреЗ рдЕрдВрдд рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХреЛрдб рдЬреЛрдбрд╝реЗрдВ

  int[] score=scoring(); if(moveNumber==0){ endTurn(score); }else { //   .      endTurn()   //       int losersCount=0; for(int i=0; i<COUNT_OF_PLAYERS; i++){ if(score[i]==0) losersCount++; } if(losersCount+1==COUNT_OF_PLAYERS){ isEndGame=true; } if(!mHandler.hasMessages(0)){ view.unlock(); endTurn(score); } } } //   private void endTurn(int[] score){ if(!isEndGame){ if(currentPlayer == COUNT_OF_PLAYERS-1){ moveNumber++; currentPlayer=0; }else { currentPlayer++; } }else{ activity.endGame(currentPlayer, score[currentPlayer]); } // ,  ,   ,      activity.setMoveNumber(moveNumber); activity.setPlayerName(currentPlayer); activity.setScore(score); } //  int[] scoring(){ int[] score=new int[COUNT_OF_PLAYERS]; for(int x=0; x<BOARD_WIDTH; x++){ for(int y=0; y<BOARD_HEIGHT; y++){ if(cells[x][y].getPlayer()!=-1){ score[cells[x][y].getPlayer()]+=cells[x][y].getCountOfAtoms(); } } } return score; } 


рдкреВрд░рд╛ рдХреЛрдб
рдпрд╣ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмрдиреА рд╣реБрдИ рд╣реИ
  void setPlayerName(int playerID){} void setScore(int[] score){} void setMoveNumber(int moveNumber){} void endGame(int winnerID, int score){} 


рдпрд╣ рд╣реЛрдорд╡рд░реНрдХ рд╣реЛрдЧрд╛ред рдпрд╣рд╛рдВ рд╕рдм рдХреБрдЫ рддреБрдЪреНрдЫ рд╣реИред

рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдХреЗ рдмрд╛рдж, рдлрд╝рд╛рдЗрд▓



рдЕрдЧрд▓рд╛, рдЖрдкрдХреЛ рдЧреЗрдо рдХреЛ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЧрддрд┐рд╡рд┐рдзрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд░рд┐рдгрд╛рдореА рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдереЛрдбрд╝рд╛ рд╕рдорд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ (рдЦреЗрд▓ рдХреЗ рдореИрджрд╛рди рдХрд╛ рдЖрдХрд╛рд░, рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛, рдкрд░рдорд╛рдгреБрдУрдВ рдХрд╛ рд░рдВрдЧ рдФрд░ рдЦрд┐рд▓рд╛рдбрд╝рд┐рдпреЛрдВ рдХреЗ рдирд╛рдо, рд▓реЗрдХрд┐рди рдпрд╣ рдЗрд╕ рд▓реЗрдЦ рдХрд╛ рд╡рд┐рд╖рдп рдирд╣реАрдВ рд╣реИред рдЖрдк рдЧреАрдердм рдкрд░ рдЕрдВрддрд┐рдо рдХреЛрдб рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, Google Play рдкрд░ рдкрд░рд┐рдгрд╛рдореА рдЧреЗрдо рдХреЛ рдЖрдЬрд╝рдорд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдпрд╣ рд▓реЗрдЦ рдХрд┐рд╕реА рдХреА рдорджрдж рдХрд░рддрд╛ рд╣реИред
рдореИрдВ рдЖрдкрд╕реЗ рдмрдбрд╝реЗ рдкрддреНрдерд░ рдирд╣реАрдВ рдлреЗрдВрдХрдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рддрд╛ рд╣реВрдВред рдореИрдВ рдХреЛрдб рдХреА рдирд┐рдореНрди рдЧреБрдгрд╡рддреНрддрд╛ рдХреЛ рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реВрдВ рдФрд░ рдЧрдВрджрдЧреА рд╕реЗ рдмрдЪрдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рд╡рд╛рджрд╛ рдХрд░рддрд╛ рд╣реВрдВред рдЖрдкрдХрд╛ рдзреНрдпрд╛рди рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред

PS рдпрд╣ рдореЗрд░рд╛ рд╣реИрдмрд░ рдкрд░ рдкрд╣рд▓рд╛ рд▓реЗрдЦ рд╣реИред

Source: https://habr.com/ru/post/In151492/


All Articles